0 c++ sse simd vectorization visual-studio
我正在使用_mm_extract_epi8 (__m128i a, const int imm8)
函数,它有const int
参数.当我编译此c ++代码时,收到以下错误消息:
错误C2057预期的常量表达式
__m128i a;
for (int i=0; i<16; i++)
{
_mm_extract_epi8(a, i); // compilation error
}
Run Code Online (Sandbox Code Playgroud)
我怎么能在循环中使用这个功能?
首先,即使可能,你也不想在循环中使用它,并且你不希望用16x完全展开循环pextrb
.该指令在Intel和AMD CPU上花费2 uop,并且将在shuffle端口(以及用于vec-> int数据传输的端口0)上出现瓶颈.
的_mm_extract_epi8
固有需要的编译时间常数索引,因为该pextrb r32/m8, xmm, imm8
指令是只适用于索引作为立即(嵌入到指令的机器代码).
如果你想放弃SIMD并在向量元素上写一个标量循环,那么你应该存储/重新加载这么多元素.所以你应该用C++编写它:
alignas(16) int8_t bytes[16]; // or uint8_t
_mm_store_si128((__m128i*)bytes, vec);
for(int i=0 ; i<16 ; i++) {
foo(bytes[i]);
}
Run Code Online (Sandbox Code Playgroud)
一个商店的成本(以及商店转发延迟)在16次重新加载中摊销,movsx eax, byte ptr [rsp+16]
每次只需1 或其他任何费用.(在英特尔和Ryzen上1次).或者使用uint8_t
的movzx
零扩展到32位的重载.现代CPU每个时钟可以运行2个负载微控制器,而矢量存储 - >标量重载存储转发是高效的(约6或7个周期延迟).
使用64位元素,movq
+ pextrq
几乎可以肯定是最好的选择.存储+重新加载与前端相比成本相当,而延迟比提取更差.
使用32位元素,根据您的循环,它更接近收支平衡.如果循环体很小,则展开的ALU提取物可能是好的.或者您可以存储/重新加载但是在第一个元素上使用_mm_cvtsi128_si32
(movd
)执行第一个元素以获得低延迟,这样CPU就可以在高元素的存储转发延迟发生时进行处理.
对于16位或8位元素,如果需要遍历所有8或16个元素,则几乎可以肯定存储/重新加载.
如果循环对每个元素进行非内联函数调用,则Windows x64调用约定具有一些调用保留的XMM寄存器,但x86-64 System V不会.所以,如果你的XMM REG将需要洒/环绕函数调用重载,这是多好,只是做标量负荷,因为编译器会在内存中有也无妨.(希望它可以优化掉它的第二个副本,或者你可以宣布一个联盟.)
请参阅 为所有元素大小的工作存储+标量循环打印__m128i变量.
如果你真的想要一个水平和,或min或max,你可以用O(log n)步骤中的shuffle来做,而不是n个标量循环迭代. 在x86上进行水平浮点矢量和的最快方法(也提到了32位整数).
对于求和字节元素,SSE2有一个特例_mm_sad_epu8(vec, _mm_setzero_si128())
. 使用英特尔上的SSE2减少无溢出的无符号字节数.
您也可以使用它来通过范围转换到无符号来进行有符号字节,然后16*0x80
从总和中减去. https://github.com/pcordes/vectorclass/commit/630ca802bb1abefd096907f8457d090c28c8327b
归档时间: |
|
查看次数: |
195 次 |
最近记录: |