加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

如何从SSE2 __m128i结构中提取字节?

发布时间:2020-12-16 10:04:10 所属栏目:百科 来源:网络整理
导读:我是SIMD内在函数的初学者,所以我会提前感谢大家的耐心等待.我有一个涉及无符号字节的绝对差异比较的应用程序(我正在使用灰度图像). 我尝试了AVX,更现代的SSE版本等,但最终决定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*));
}

(上面的代码假设xres和yres是相同的,并且是2的幂).这会变成内存中的线性,不间断的块吗?那么,当我循环时,我可以继续将地址(递增128)传递给SSE2加载内在函数吗?或者像这样的2D阵列需要做些什么?

我的第二个问题:一旦我完成了所有的矢量处理,我如何从__m128i中提取修改后的字节?通过英特尔内在指南,将矢量类型转换为标量类型的指令很少见.我发现的最接近的是int _mm_movemask_epi8(__ m128i a),但我不太明白如何使用它.

哦,还有第三个问题 – 我假设_mm_load_si128只加载有符号字节?而且我找不到任何其他字节加载函数,所以我猜你应该从每个中减去128并稍后解释它?

我知道这些是SIMD专家的基本问题,但我希望这对像我这样的初学者有用.如果你认为我对应用程序的整个方法是错误的,或者我会更好地使用更现代的SIMD扩展,我很想知道.我只是想谦虚地警告我从未使用过装配,所有这些有点蠢蠢欲动的东西需要大量的解释才能帮助我.

不过,我很感激任何澄清.

如果它有所不同:我的目标是低功耗的i7 Skylake架构.但是,将应用程序运行在更旧的机器上也是很好的(因此SSE2).

解决方法

最不明显的问题是:

once I’ve done all my vector processing,how the heck do I extract the modified bytes from the __m128i

使用int64_t _mm_cvtsi128_si64x(__ m128i)或the low 32 bits with int _mm_cvtsi128_si32 (__m128i a)将低64位提取为整数.如果需要向量的其他部分,则选项为:

>随机移动矢量以使用低元素中所需的数据创建新的__m128i,并使用cvt内在函数(asm中的MOVD或MOVQ).
>对于其他元素大小,请使用SSE2 int _mm_extract_epi16 (__m128i a,int imm8)或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的高半部分

Does this turn into a linear,unbroken block in memory?

不,它是一个指向内存块的指针数组,引入了额外的间接级别与正确的2D阵列.不要那样做.

进行一次大型分配,并自己进行索引计算(使用array [x * yres y]).

是的,使用_mm_load_si128从中加载数据,或者如果需要从偏移量加载,则加载数据.

assumed _mm_load_si128 only loads signed bytes

有符号或无符号不是字节的固有属性,它只是你如何解释这些位.您使用相同的加载内在函数来加载两个64位元素或128位位图.

使用适合您数据的内在函数.它有点像汇编语言:一切都只是字节,机器会用你的字节来做你所说的.您可以选择一系列指令/内在函数来产生有意义的结果.

整数加载内在函数采用__m128i *指针args,因此必须使用_mm_load_si128((const __m128i *)my_int_pointer)或类似的.这看起来像指针别名(例如,通过短*读取int数组),这是C和C中的未定义行为.但是,这就是英特尔表示您应该这样做的方式,因此任何实现英特尔内在函数的编译器都需要使其正常工作. gcc通过使用__attribute __((may_alias))定义__m128i来实现.

另请参阅Loading data for GCC’s vector extensions,其中指出您可以将英特尔内在函数用于GNU C本机向量扩展,并显示如何加载/存储.

要了解有关使用SSE的SIMD的更多信息,sse标记wiki中有一些链接,包括一些介绍/教程链接.

x86标签wiki有一些很好的x86 asm / performance链接.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读