sac*_*eie 3 c image-processing simd vectorization sse2
我是SIMD内在函数的初学者,所以我会提前感谢大家的耐心等待.我有一个涉及无符号字节的绝对差异比较的应用程序(我正在使用灰度图像).
我尝试了AVX,更现代的SSE版本等,但最终决定SSE2似乎已经足够并且对个别字节的支持最多 - 如果我错了,请纠正我.
我有两个问题:第一,加载128位寄存器的正确方法是什么?我想我应该将加载的内在数据传递到128的倍数,但这样可以使用2D数组代码:
greys = aligned_alloc(16, xres * sizeof(int8_t*));
for (uint32_t x = 0; x < xres; x++)
{
greys[x] = aligned_alloc(16, yres * sizeof(int8_t*));
}
Run Code Online (Sandbox Code Playgroud)
(上面的代码假设xres和yres是相同的,并且是2的幂).这会在内存中变成线性的,不间断的块吗?那么,当我循环时,我可以继续将地址(递增128)传递给SSE2加载内在函数吗?或者像这样的2D阵列需要做些什么?
我的第二个问题:一旦我完成了所有的矢量处理,我该如何从中提取修改后的字节__m128i?通过英特尔内在指南,将矢量类型转换为标量类型的指令很少见.我发现的最接近的是int,_mm_movemask_epi8 (__m128i a)但我不太明白如何使用它.
哦,还有三分之一的问题 - 我假设_mm_load_si128只加载了带符号的字节?而且我找不到任何其他字节加载函数,所以我猜你应该从每个中减去128并稍后解释它?
我知道这些是SIMD专家的基本问题,但我希望这对像我这样的初学者有用.如果你认为我对应用程序的整个方法是错误的,或者我会更好地使用更现代的SIMD扩展,我很想知道.我只是想谦虚地警告我从未使用过装配,所有这些有点蠢蠢欲动的东西需要大量的解释才能帮助我.
不过,我很感激任何澄清.
如果它有所不同:我的目标是低功耗的i7 Skylake架构.但是,将应用程序运行在更旧的机器上也是很好的(因此SSE2).
最不明显的问题是:
一旦我完成了所有的矢量处理,我该如何从中提取修改后的字节
__m128i
将低64位提取为整数int64_t _mm_cvtsi128_si64x(__m128i),或者将低32位int _mm_cvtsi128_si32 (__m128i a)提取 .如果你想要矢量的其他部分,你的选择是:
__m128i在low元素中创建具有所需数据的new ,并使用cvt intrinsics(asm中的MOVD或MOVQ).int _mm_extract_epi16 (__m128i a, int imm8)对于其他元素大小,请使用SSE2 或SSE4.1类似指令.PEXTRB/W/D/Q不是最快的指令,但如果你只需要一个高元素,它们比单独的shuffle和MOVD更好.union { __m128i v; int64_t i64[2]; }或东西.基于联合的类型惩罚在C99中是合法的,但仅作为C++中的扩展.在C++中也可以使用的联合的替代方法是memcpy(&my_int64_local, 8 + (char*)my_vector, 8);提取高半部分.希望这不会编译到商店+实际的memcpy库函数调用.由于像这样的用例,编译器通常非常适合优化小的固定大小的memcpy,但是你可能会得到一个存储/重新加载而不是PEXTRQ,即使你编译时-msse4.1让编译器在需要时使用它.如果结果可以直接进入内存未修改(而不是在整数寄存器中需要),智能编译器可能会使用MOVHPS来存储高半部分__m128i这会在内存中变成线性的,不间断的块吗?
不,它是一个指向内存块的指针数组,引入了额外的间接级别与正确的2D阵列.不要那样做.
进行一次大型分配,然后自己进行索引计算(使用array[x*yres + y]).
是的,_mm_load_si128如果需要从偏移量加载,则从中加载数据,或者加载数据.
假设
_mm_load_si128只加载有符号字节
有符号或无符号不是字节的固有属性,它只是你如何解释这些位.您使用相同的加载内在函数来加载两个64位元素或128位位图.
使用适合您数据的内在函数.它有点像汇编语言:一切都只是字节,机器会用你的字节来做你所说的.您可以选择一系列指令/内在函数来产生有意义的结果.
整数加载内在函数采用__m128i*指针args,因此您必须使用_mm_load_si128( (const __m128i*) my_int_pointer )或类似.这看起来像指针别名(例如,int通过a 读取数组short *),这是C和C++中的未定义行为.但是,这就是英特尔表示您应该这样做的方式,因此任何实现英特尔内在函数的编译器都需要使其正常工作.gcc通过定义__m128i来实现__attribute__((may_alias)).
另请参阅加载GCC向量扩展的数据,指出您可以将英特尔内在函数用于GNU C本机向量扩展,并说明如何加载/存储.
要了解有关使用SSE的SIMD的更多信息,sse标记wiki中有一些链接,包括一些介绍/教程链接.
在x86的标签wiki有一些很好的x86 ASM /性能链接.
| 归档时间: |
|
| 查看次数: |
3080 次 |
| 最近记录: |