该参数主要是用来存放Global Memory(外部存储)的全局数据。
定义原型:
template class GlobalTensor {
// 传入全局数据的指针,并手动设置一个buffer size,初始化GlobalTensor
void SetGlobalBuffer(__gm__ T* buffer, uint32_t bufferSize);
}
其中buffer--->主机侧传入的全局数据指针指向外部存储的起始地址。
其中buffersize--->设置GlobalTensor所包含的类型为T的数据个数,需自行保证不会超出实际数据的长度。
该参数主要是用来存放核上Local Memory(内部存储)的数据。
定义原型:
template class LocalTensor {
T GetValue(const uint32_t offset) const; // 获取 LocalTensor 中的某个值,返回 T 类型的立即数。
template void SetValue(const uint32_t offset, const T1 value) const; // 设置 LocalTensor 中的某个值。offset单位为element
// 获取距原LocalTensor起始地址偏移量为offset的新LocalTensor,注意offset不能超过原有LocalTensor的size大小。offset单位为element
LocalTensor operator[](const uint32_t offset) const;
uint32_t GetSize() const; // 获取当前LocalTensor size大小
}
// input_local长度设置为256个int32_t类型的数据
// 示例1:对input_local中第i个位置进行赋值为100
for (int32_t i = 0; i < 256; ++i) {
input_local.SetValue(i, 100);
}
// 结果:input_local为256个100 [100 100 100 ... 100]
// 示例2:获取input_local中第19个位置的数值
auto element = input_local.GetValue(19);
// 结果:element为100
// 示例3:获取input_local的长度,size大小为input_local有多少个element
auto size = input_local.GetSize();
// 结果:size大小为256
// 示例4:operator[]使用方法,output_local[50]为从起始地址开始偏移量为50的新tensor
Add(output_local[50], input_local[50], input_local2[50], 50);
// 结果如下:
// 输入数据(input_local): [100 100 100 ... 100] 50个int32_t
// 输入数据(input_local2): [1 2 3 ... 50] 50个int32_t
// 输出数据(output_local): [101 102 103 ... 150] 50个int32_t
矢量计算指令接口,能够启动AI Core中的Vector单元执行计算
为了降低开发者的使用门槛,指令按照由易到难,分成了3级到0级接口。其中3级接口最为简单,0级接口最为复杂,(1级接口还未发布)
多层级API封装的作用:
1、降低复杂指令的使用难度
2、跨代兼容性保障
3、保留最大灵活度的可能
单目指令操作:Exp、Ln、Abs、Reciprocal、Sqrt、Rsqrt、Not、Relu、Sigmoid、Tanh、…
双目指令操作:Add、Sub、Mul、Div、Max、Min、And、Or、…
标量双目指令操作:Adds、Muls、Maxs、Mins、ShiftLeft、ShiftRight、LeakyRelu、…
标量三目指令操作:Axpy、…
比较指令操作:Compare、…
选择指令操作:Select、ReduceV2、…
精度转换指令操作:Cast、…
规约指令操作:ReduceMax、ReduceMin、ReduceSum、…
特殊规约指令操作:WholeReduce、BlockReduce、PairReduce、…
数据转换操作:Transpose、TransDataTo5HD、…
数据填充操作:Duplicate、Brcb、…
数据搬移操作:DataCopy、Copy、…
……
3级接口,运算符重载,支持+, -, *, /, |, &, ^, >, < , >=, <=,!=,==实现2级接口的简化表达 允许用户使用形如:dst = src0 ※ src1,针对整个Tensor进行计算。
以下指令API拥有3级接口:
Add:dstLocal = src0Local + src1Local;
Sub:dstLocal = src0Local - src1Local;
Mul:dstLocal = src0Local * src1Local;
Div:dstLocal = src0Local / src1Local;
And:dstLocal = src0Local & src1Local;
Or:dstLocal = src0Local | src1Local;
Compare:
dstLocal = src0Local < src1Local;
dstLocal = src0Local > src1Local;
dstLocal = src0Local <= src1Local;
dstLocal = src0Local >= src1Local;
dstLocal = src0Local == src1Local;
dstLocal = src0Local != src1Local;
注意:三级接口会进行连续矢量运算,运算量为目的LocalTensor的总长度。
2级连续计算接口,针对源操作数srcLocal的连续COUNT个数据进行计算,并连续写入目的操作数dstLocal,提供了一维Tensor的连续COUNT个数据的计算支持。
允许用户使用形如:
void Operator(const LocalTensor& dstLocal, const LocalTensor& srcLocal, const int32_t& calCount)
大多数指令API拥有2级接口,2级接口相对于3级接口,可以自定义运算量。
Exp(dstLocal, srcLocal, 512);
Adds(dstLocal, srcLocal, scalarValue, 512);
Select(dstLocal, maskLocal, src0Local, src1Local, SELMODE::VSEL_CMPMASK_SPR, 256);
ReduceMin(dstLocal, srcLocal, workLocal, 8320, true);
Duplicate(dstLocal, inputVal, 256);
注意:二级接口会进行连续矢量运算,开发者指定的运算量不能超过参与运算Tensor本身的大小。
0级功能灵活计算接口,是最底层的开发接口,可以完整发挥硬件优势的计算API,可以进行非连续的计算 该功能可以充分发挥CANN系列芯片的强大功能指令,支持对每个操作数的Block stride,Repeat stride,MASK的操作,允许用户使用诸多的通用参数来定制化所需要的操作。
通用参数包括:
Repeat times(迭代的次数)
Block stride(单次迭代内不同block间地址步长)
Repeat stride(相邻迭代间相同block的地址步长)
Mask(用于控制参与运算的计算单元)
struct UnaryRepeatParams {
uint32_t blockNumber = kDefaultBlkNum;
uint16_t dstBlkStride = kDefaultBlkStride;
uint16_t srcBlkStride = kDefaultBlkStride;
uint8_t dstRepStride = kDefaultRepStride;
uint8_t srcRepStride = kDefaultRepStride;
bool repeatStrideMode = false;
bool strideSizeMode = false;
bool half_block = false;
};
Mask逐比特模式:
template __aicore__ inline void Exp(const LocalTensor& dstLocal, const LocalTensor& srcLocal, uint64_t mask[2], const uint8_t repeatTimes, const UnaryRepeatParams& repeatParams);
Mask连续模式:
template __aicore__ inline void Exp(const LocalTensor& dstLocal, const LocalTensor& srcLocal, uint64_t mask, const uint8_t repeatTimes, const UnaryRepeatParams& repeatParams);
矢量计算单元,一次最多可以计算256Bytes的数据,每次读取连续的8个block(每个block 32Bytes,共256Bytes)数据进行计算,为完成对输入数据的处理,必须通过多次迭代(repeat)才能完成所有数据的读取与计算。 待处理数据大小为16个block(512Bytes),每次迭代处理8个block(256Bytes),需要两次迭代完成计算,Repeat times应设置为2。
当Repeat times大于1,需要多次迭代完成矢量计算时,可以根据不同的使用场景合理设置相邻迭代间相同block的地址步长Repeat stride的值。
连续计算场景:假设定义一个Tensor供目的操作数和源操作数同时使用(即地址重叠),Repeat stride取值为8。此时,矢量计算单元第一次迭代读取连续8个block,第二轮迭代读取下一个连续的8个block,通过多次迭代即可完成所有输入数据的计算。
非连续计算场景:Repeat stride取值大于8(如取10)时,则相邻迭代间矢量计算单元读取的数据在地址上不连续,出现2个block的间隔。
当Repeat times大于1,需要多次迭代完成矢量计算时,可以根据不同的使用场景合理设置相邻迭代间相同block的地址步长Repeat stride的值。
反复计算场景:Repeat stride取值为0时,矢量计算单元会对首个连续的8个block进行反复读取和计算。
部分重复计算:Repeat stride取值大于0且小于8时,相邻迭代间部分数据会被矢量计算单元重复读取和计算,此种情形一般场景不涉及。
Block stride表示同意迭代内不同block的地址步长 如果需要控制单次迭代内,数据处理的步长,可以通过设置同一迭代内不同block的地址步长Block stride来实现。 连续计算,Block stride 设置为1,对同一迭代内的8个block数据连续进行处理 非连续计算,Block stride值大于1(如取2),同一迭代内不同block之间在读取数据时出现一个block的间隔。
Mask用于控制每次迭代内参与计算的元素。可通过连续模式和逐比特模式两种方式进行设置。
连续模式:表示前面连续的多少个元素参与计算。数据类型为uint64_t。取值范围和操作数的数据类型有关,数据类型不同,每次迭代内能够处理的元素个数最大值不同(当前数据类型单次迭代时能处理的元素个数最大值为:256 / sizeof(数据类型))。当操作数的数据类型占比特位16位时(如half,uint16_t),mask∈[1, 128];当操作数为32位时(如float, int32_t),mask∈[1, 64]。
// int16_t数据类型单次迭代能处理的元素个数最大值为256/sizeof(int16_t) = 128,mask = 64,mask∈[1, 128],所以是合法输入
// repeatTimes = 1, 共128个元素,单次迭代能处理128个元素,故repeatTimes = 1
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入
uint64_t mask = 64;
Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });
结果示例如下:
输入数据(src0Local): [1 2 3 ... 64 ...128]
输入数据(src1Local): [1 2 3 ... 64 ...128]
输出数据(dstLocal): [2 4 6 ... 128 undefined...undefined]
// int32_t数据类型单次迭代能处理的元素个数最大值为256/sizeof(int32_t) = 64,mask = 64,mask∈[1, 64],所以是合法输入
// repeatTimes = 1, 共64个元素,单次迭代能处理64个元素,故repeatTimes = 1
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入
uint64_t mask = 64;
Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });
结果示例如下:
输入数据(src0Local): [1 2 3 ... 64]
输入数据(src1Local): [1 2 3 ... 64]
输出数据(dstLocal): [2 4 6 ... 128]
逐比特模式:可以按位控制哪些元素参与计算,比特位的值为1表示参与计算,0表示不参与。参数类型为长度为2的uint64_t类型数组 参数取值范围和操作数的数据类型有关,数据类型不同,每次迭代内能够处理的元素个数最大值不同。当操作数为16位时,mask[0]、mask[1]∈[0, 264-1];当dst/src为32位时,mask[1]为0,mask[0]∈[0, 264-1]。
// 数据类型为int16_t
uint64_t mask[2] = {6148914691236517205, 6148914691236517205};
// repeatTimes = 1, 共128个元素,单次迭代能处理128个元素,故repeatTimes = 1。
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据。
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入。
Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });
结果示例如下:
输入数据(src0Local): [1 2 3 ... 64 ...127 128]
输入数据(src1Local): [1 2 3 ... 64 ...127 128]
输出数据(dstLocal): [2 undefined 6 ... undefined ... 254 undefined]
// 数据类型为int32_t
uint64_t mask[2] = {6148914691236517205, 0};
// repeatTimes = 1, 共64个元素,单次迭代能处理64个元素,故repeatTimes = 1。
// dstBlkStride, src0BlkStride, src1BlkStride = 1, 单次迭代内连续读取和写入数据。
// dstRepStride, src0RepStride, src1RepStride = 8, 迭代间的数据连续读取和写入。
Add(dstLocal, src0Local, src1Local, mask, 1, { 1, 1, 1, 8, 8, 8 });
结果示例如下:
输入数据(src0Local): [1 2 3 ... 63 64]
输入数据(src1Local): [1 2 3 ... 63 64]
输出数据(dstLocal): [2 undefined 6 ... undefined ... 126 undefined]