现代C++ [1]: 智能指针
简介
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象(类)。当栈对象的生存周期结束后(离开对象的作用域,如函数结束),会在析构函数中释放掉申请的内存,不需要手动释放内存空间,从而规避内存泄漏的风险。
auto_ptr
auto_ptr是C++ 98标准的方案,在C++ 11标准中已经弃用,采用独占所有权模式,看下面这个例子:
std::auto_ptr<string> p1 (new string ("Hello World")); std::auto_ptr<string> p2; p2 = p1; // 编译不会报错,但p1会被置成nullptr
auto_ptr的赋值操作在编译时不会报错,但p2剥夺了p1的所有权,使得p1成为一个空指针。所以当程序运行时访问p1将会报错。这也是促使auto_ptr的被弃用的原因。
unique_ptr
作为接替auto_ptr的独占资源所有权的智能指针,其特点如下:
独占式拥有或严格拥有, 保证同一时间内只有一个智能指针可以指向该对象;
常见用法:使用 std::unique ...
Effective C++ 读书笔记02
前言
本文是阅读《Effective C++ 改善程序与设计的55个具体做法(第三版)》的心得笔记第二部分,文章也会按照原书的顺序依次记录各个条款。
第一部分的阅读笔记参见effective C++ 读书笔记01。
资源管理
C++中最常使用的资源就是动态内存分配,除此之外,其他常见的资源还有文件描述符(file descriptors)、互斥锁(mutex locks)、图形界面中的字型和笔刷、数据库连接和网络sockets等。无论是何种资源,当不再使用它时,必须将其归还给系统。
条款13:以对象管理资源
关于资源泄漏的风险,考虑如下这样一种场景:
void f { Investment* pInv = createInvestment(); // 工厂函数创建对象并返回对象指针 ... delete pInv; // 释放pInv指针所指对象}
上面代码的问题在于可能由于...中的某些操作导致代码无法执行到delete语句,比如:
...中存在某种特定的判断逻辑会提前触发return语句;
delete语句存在于某个循环当中,而该循环又存在cont ...
PyTorch源码解读 [1]: 编译源码
前言
为了更好的理解,阅读和调试PyTorch后端的C++源码实现,需要从源码开始编译PyTorch。
参考文档:PyTorch官方github的安装指引
Windows编译(已放弃)
已有环境如下:
操作系统:windows11
conda 4.9.2
python3.8
VS studio 2019 16.11.3
CUDA 10.2
cudnn 8.1.1.33
ninja 1.10.2
准备工作
新建conda环境
conda info -econda create -c conda-forge -n PyTorchCompile python=3.8conda info -e
安装依赖
激活conda环境,注意不要用PowerShell,不支持Conda虚拟环境。
conda activate PyTorchCompileconda install astunparse numpy ninja pyyaml mkl mkl-include setuptools cmake cffi typing_extensions future six requests datacla ...
C++之类与对象 [2]: 类的成员
常数据成员
值在程序运行期间不可变
定义格式:const 类型 数据成员名称;
初始化:只能通过构造函数中的初始化列表进行
常成员函数
不能修改对象成员值的函数
定义格式:类型 成员函数名称(参数列表) const;
常成员函数不能调用类中非常成员函数
静态成员函数不能定义为常成员函数
如果对象为常量,则只能调用其常成员函数
静态数据成员
静态数据成员只有一份,由该类所有对象共享
声明格式:static 类型 静态数据成员名称;
仅声明,不在对象上分配空间
定义格式:类型 类名称::静态数据成员名称 = 初始值;
必须在外部初始化,初始化动作与访问控制无关
静态成员函数
在类而不是对象上调用
目的:访问类的静态数据成员,若要访问类的非静态数据成员,必须指定对象或者使用指向对象的指针(静态成员函数没有缺省的this指针)
// 类声明class A{public:static int f();static int g( const A & a );private:static int count;int num;};// 类定义int ...
Effective C++ 读书笔记01
前言
本文是阅读《Effective C++ 改善程序与设计的55个具体做法(第三版)》的心得笔记,文章也会按照原书的顺序依次记录各个条款。
让自己习惯C++
条款01:视C++为一个语言联邦
C++在发展中逐渐成为多种次语言的集合:
C语言:C++的基础,区块、语句、预处理器、内置数据类型、数组和指针等特性都来自C;
Object-Oriented C++:面向对象编程的特性,主要包括类、封装、继承、多态和virtual函数(动态绑定)等;
Template C++:泛型编程(generic programing)的特性,带来了模板元编程(template metaprogramming,TMP)
STL(Standard Template Library,标准模板库):template程序库,其封装了容器(containers)、迭代器(iterators)、算法(algorithms)和函数对象(function objects)等
条款02:尽量以const,enum,inline替换 #define
除了担任控制编译外,尽量减少在c++源代码中使用#define。即尽量以 ...
AMBA开发 [1]:CVflow 编程
前言
安霸CVflow处理器单元是一个为视觉算法设计的的硬件协处理器(VPU/NPU),使用有向无环图(DAG,directed acyclic graph)来表示视觉算法。
概念介绍
DAG
DAG是一个有向无环图,视觉算法的计算流的图表示。图中的**节点(Nodes)**表示运算操作,连线(Links)表示运算操作之间的数据流。数据由多维张量组成,这里简称为向量(Vectors)。
CVflow
DAG表示的视觉算法在CVflow处理器上执行。DAG包含了关于I/O buffers、Nodes(又称为Primitives)以及Links的描述符。
CVflow上没有程序计数器或者指令pipeline的概念,一个主调度器会基于图中的数据依赖关系对可用的硬件资源上的原语(primitives)进行分时复用的调度。在CVflow上,如果下游原语依赖的数据已经准备好,那么该原语会基于硬件可用与否被并行执行。这种部分执行(partial execution)允许更有效地调度硬件资源,并允许片上内存在原语之间传递结果,而不是从外部DRAM存储和重新加载这些结果。
原语之间的依赖关系在DAG程序 ...
SNPE开发[1]: GetStart
前言
硬件:创通联达以高通QCS610作为SOC的开发板TurboX C610
SDK:Qualcomm Neural Processing SDK for AI v1.59.0
docs:Reference Guide
骁龙神经处理引擎(SnapDragon Neural Processing Engine)是高通用于加速神经网络的运行时推理框架。
Setup
必备条件
目前SNPE SDK开发环境仅支持Ubuntu-18.04;
支持Caffe,Caffe2,ONNX,PyTorch,TensorFlow,TFLite;
Python3.6
Android NDK(可选):用于构建SDK附带的Cpp示例
Android SDK(可选):用于构建 SDK附带的APK
安装docker-ubuntu18.04
docker pull ubuntu:18.04docker container run -itd --name snpe_1.59.0 ubuntu:18.04docker exec -it snpe_1.59.0 /bin/bash
安装python3.6
换apt- ...
计算机体系结构 [6]:存储器层次结构
前言
存储器的层次结构如下图所示:
各类存储器的成本和延时对比如下图,一个历年硬件设备延时变化的链接:
一个简单的类比如下图所示:
存储器层次结构的设计并不仅受成本层面的限制,更受物理层面的限制。L1 Cache不仅昂贵,其访问速度和它到 CPU 的物理距离有关。仅靠堆料并不能打破电信号不能超过光速的物理限制。
SRAM/Cache
**SRAM(**Static Random-Access Memory,静态随机存取存储器),所谓静态,是只要处在通电状态,里面的数据就可以保持存在。而一旦断电,里面的数据就会丢失了。特点:存储密度不高(一个比特需要6-8个晶体管),但电路简单,访问速度非常快,一个6个晶体管组成一个比特的SRAM的示意图如下:
Cache通常分为L1、L2、L3三级:
L1 Cache:嵌在每个CPU核心内部,访问速度最快。通常分成指令缓存和数据缓存(借鉴哈佛结构解决结构冒险问题);
L2 Cache:同样每个CPU核心独有,但不在核心内部,访问速度略慢。
L3 Cache:多个CPU核心共用,尺寸更大,但访问速度更慢。
为什么需要Cache?
随着硬件的发 ...
AI算法基础 [17]:快速浮点exp算法
前言
本文将介绍一种利用IEEE754标准浮点数表示方法,在精度损失很小的情况下,优化指数运算exp的性能的算法。
浮点数表示
根据国际标准IEEE 754,任意(规格化形式)一个二进制浮点数Y可以表示成下面的形式:
Y=(−1)S×(1+M)×2XY=(-1)^S×(1+M)×2^X
Y=(−1)S×(1+M)×2X
其中,X=E−biasX=E-biasX=E−bias。
对于32位的浮点数(单精度浮点数),最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
关于各部分的解释如下:
S$$表示符号位,当$$S=0$$时$Y$为正数;当$$S=1$$时$Y$为负数。
X=E-bias$$表示指数位。$$E$$的规则具体如下:
- 首先,$$E$$为一个无符号整数。这意味着,如果$$E$$为8位,它的取值范围为0~255;如果$$E$$为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的$$E$$是可以出现负数的。**所以IEEE 754规定,$E ...
计算机体系结构 [0]:杂项
前言
这里将收录一些不成体系的,不值得用一整个文章篇幅去探讨的,或者暂时只关注上层定义的关于计算机体系结构的杂项。每个小节之间的关联性不强,仅做快速查阅之用。
CISC与RISC
定义
复杂指令集(Complex Instruction Set Computing,CISC),代表就是Intel的x86架构
精简指令集(Reduced Instruction Set Computing,RISC),代表就是ARM架构
两种架构的优化思路不同:
程序的CPU执行时间=指令数×CPI×Clock Cycle Time程序的CPU执行时间 = 指令数 × CPI × Clock \space Cycle \space Time
程序的CPU执行时间=指令数×CPI×Clock Cycle Time
CISC 架构是通过优化指令数,来减少 CPU 的执行时间。而 RISC 架构是在优化 CPI,因为指令比较简单,需要的时钟周期就比较少。
Intel的反击
CISC的设计是有受限于硬件条件不足的历史因素的影响的,但随着硬件的发展,内存和寄存器变得越来越多,像CISC这种为了更高效(充分)利用 ...