【发布时间】:2017-09-27 09:35:35
【问题描述】:
类似于this question,我想将几个 24 位值收集到 SSE/AVX 寄存器的 32 位双字中。进一步:
- 每个值与基指针的偏移量不连续
- 每个值的偏移量只有 1 字节对齐
- 我可以确保在每个值之后(或之前)读取向量是安全的
AVX2(高性能?)收集解决方案还可以,但我还需要预 AVX 支持。看起来带有指示 1 字节对齐的 SIB 字节的 pinrd 正是我想要的,但我不知道如何让编译器发出这个指令编码......
使用标准内在函数:
uint32_t *p = &base[offset];
vec = _mm_insert_epi32(vec, *p, 1); // for each dword...
产生合理的编码,假设偏移对齐:
660f3a2244_b5_0001 pinsrd $0x1, (%rbp,%rsi,4), %xmm0
但是,我想实际发出:
660f3a2244_35_0001 pinsrd $0x1, (%rbp,%rsi), %xmm0
并手动将偏移量预乘 3。
这种编码(通过十六进制编辑链接的二进制文件进行测试)似乎工作得很好。但是......我怎么能发出它?没有多少类型转换或属性
__align__ 似乎有效。显而易见的方法:
uint8_t *p = &base[offset*3];
vec = _mm_insert_epi32(vec, *p, 1);
当然,在插入之前会将一个零扩展名的字节取消引用到一个双字。
我的内联 asm 尝试:
static inline __m128i __attribute__((always_inline))
_mm_insertu_epi32(__m128i a, void *b, long o, const int8_t imm8)
{
__asm__("pinsrd %3, (%1, %2), %0" : "+x"(a) : "r"(b), "r"(o), "i"(imm8));
return a;
}
产量:
660f3a22041601 pinsrd $0x1, (%rsi,%rdx), %xmm0
这是有希望的,但似乎完全混淆了优化器;所有周围的代码都被扰乱得面目全非。
有没有办法在没有纯 asm 的情况下做到这一点? (我想使用内在的...)
【问题讨论】:
-
为什么?整个方法大部分都被破坏了,如果您只想将打包的 24 位扩展到 32 位,请加载一堆(像素?)并打乱它们。
-
@harold,你能澄清一下“破碎”吗?您的意思是“效率低下”还是“功能不正确”(由于实际 CPU 的一些对齐限制?)至于为什么,只是将 24 位值收集到向量中以进行进一步处理。在 AVX2 之前,pinsrd 似乎是最好的指令: * 使用索引寻址从内存加载 * 到向量中的任意位置 但是,与 vpgatherdd 不同,没有办法通过 instrinsic 直接控制索引比例?如果有更好(有效,正确)的方法可以从几个未对齐的偏移量中收集到矢量元素,请教我。
-
如果你只发布上下文可能最简单
-
@harold,它们没有打包。这就是我所说的“每个值都处于非连续偏移量”的意思。所以这是一个聚会。
-
很公平。尽管如此,整个 scale=1 的事情是完全没有必要的,你可以将任何东西放入内在函数中,它会正常工作。最好将 movd 和 pinsrd 混合使用,以避免完全序列化 pinrd 烦人的高延迟。因此,2x movd,2x pinsrd,将两半与拆包结合起来。如果您只是 _mm_set 一切,FWIW GCC 会自动执行此操作。这不是我通常推荐的,但在这种情况下它有点道理。如果可能的话,真的应该首先避免这种情况。
标签: c bit-manipulation sse intrinsics