Intrinsics函数踩坑,感觉用Intrinsics函数,最重要的是不能想当然,只有想不到。。还是得参考官方文档来
一系列乘法MUL——_mm256_mul_epi32
这个乘法会把32位的两个整数扩展为64位再相乘,最后结果竟然存成64位!!如果要存为32位的数,应该用下面的函数_mm256_mullo_epi32
一系列set——_mm256_set_epi32
set函数的都是这样,参数顺序是反的。而且似乎性能巨差
看汇编可以发现,set是调用了多轮insertf指令,一点点构建向量。
整数load——_mm512_load_epi32
第一次做把int数组加载到zmm寄存器中的时候,想当然的用了_mm512_load_epi32这个函数,结果编译的时候报错,找不到对应的函数,才发现load一个int类型的数组要用这个函数_mm512_loadu_si512
一看到这个函数,我就发现自己格局小了,,反正是load连续512位的内存,何必区分位数呢。
进一步想,为什么浮点数类型就要区分精度了呢?推测是__mm512i和__mm512两个结构的区别。
loadu和load
区别很明显,load要求内存对齐,而loadu不要求,似乎是在汇编层面上做了什么操作?
内存地址对齐的方法:
1.利用struct,挺多博客讲的,不过似乎不同编译器对齐原则稍有差别(需要考证),附一篇写的很好的博客
2.#pragma pack预编译指令
3._mm_free与_mm_malloc
4.手动对齐,下面的代码通过先开辟空间再手动对齐的方式,完成了对pvbQueryProf、pvsQueryProf、pvH1、pvH2、pvE空间的分配
pSwData->pData = (unsigned char *) calloc (nCount, sizeof (__m256i));
if (!pSwData->pData) {
fprintf (stderr, "Unable to allocate memory for SW data buffers\n");
exit (-1);
}
/* since we might port this to another platform, lets align the data */
/* to 16 byte boundries ourselves */
aligned = ((size_t) pSwData->pData + 15) & ~(0x0f);//对齐
pSwData->pvbQueryProf = (__m256i *) aligned;
pSwData->pvsQueryProf = pSwData->pvbQueryProf + lenQryByte * ALPHA_SIZE;
pSwData->pvH1 = pSwData->pvsQueryProf + lenQryShort * ALPHA_SIZE;
pSwData->pvH2 = pSwData->pvH1 + lenQryShort;
pSwData->pvE = pSwData->pvH2 + lenQryShort;
shift
_mm128_slli_si128函数是将128位的xmm寄存器整体左移n个byte,而_mm256_slli_si256是将两个256位的ymm寄存器按128位为一个整体左移。
可以通过如下的形式实现了256位寄存器的整体左移与右移操作,其中A为源寄存器,N为移动的字节数:
左移:
_mm256_alignr_epi8(A, _mm256_permute2x128_si256(A, A, _MM_SHUFFLE(0, 0, 2, 0)), 16 - N)
右移:
_mm256_alignr_epi8(_mm256_permute2x128_si256(A, A, _MM_SHUFFLE(2, 0, 0, 1)), A, N)
不规则内存读写——gather和scatter
非常牛的两种CPU特性,gather和scatter,一个读一个写,具体看图
类型转换
最有用的是cvt系列的指令,比如_mm256_cvtepi16_epi32
还有extractf,可以用来提取向量中的一部分
一个了解向量化指令的方法
从一位学长那学来的,可以写一个方便自动向量化的简单循环,看一下这个循环的汇编即可