如何编写编译器可以针对 SIMD 比较优化的代码? [复制]

Posted

技术标签:

【中文标题】如何编写编译器可以针对 SIMD 比较优化的代码? [复制]【英文标题】:How to write code that compiler can optimize to SIMD compare? [duplicate] 【发布时间】:2018-03-03 09:32:21 【问题描述】:
std::array<int, 4> a = 1, 1, 1, 1;
std::array<int, 4> b =  1, 2, 3, 4 ;
std::array<int, 4> c;
bool res = false;
for (int i = 0; i < a.size(); i++) 
    a[i] = rand() % 10;


for (int i = 0; i < 4; i++) 
    c[i] = a[i] + b[i];

智能编译器可以很好地编译成SIMD。 但是如何编写下面的比较代码也可以很好地编译为 SIMD;

res = a[0] <= b[0] && a[1] <= b[1] && a[2] <= b[2] && a[3] <= b[3]; // not compile to SIMD

【问题讨论】:

您使用什么编译器和编译器选项? 如果你使用 gcc 可能是__attribute__ ((vector_size (16))) Visual Studio 2015,x64,完全优化 (/Ox),高级矢量扩展 2 (/arch:AVX2),@BasileStarynkevitch 我不抱太大希望,这需要movmskps-ing 从向量中比较结果并对其进行标量比较,这不是我见过 MSVC 在其上做的事情拥有。 Auto-Vectorize comparison, Auto-vectorization of loop containing comparisons, how to auto vectorization array comparison function 【参考方案1】:

这样的事情怎么样:

int res = 0;
#pragma omp simd reduction(+:res)
for (int i = 0 ; i < 4 ; i++) 
  res += a[i] < b[i];

?

如果您可以正确对齐输入(并在 openmp 编译指示中添加对齐子句),那么它应该很快。特别是如果您的输入真的超过 4 个元素。

res 将是 0-4 而不是 0 或 1,但这可能不是问题。 SIMD 指令倾向于处理水平加法而不是水平位与。

【讨论】:

SIMD 指令倾向于处理水平添加 呃,不是 x86。存在 hadd 指令,但它只执行水平对,而不是整体减少。更重要的是,它比单独的 shuffle + add 指令要慢。 ***.com/questions/6996764/…。无论如何,在 x86 上,OP 想要的最佳 asm 是 pcmpgtd xmm0, xmm1 (a,b) / pmovmskb eax, xmm0 / cmp eax, 0xffff / je condition_true (即检查 a 的每个元素比较大于b,所以比较掩码是唯一的) 所以你真正想要的是一个知道如何使用 SIMD 比较自己的智能编译器,否则我不确定有没有办法让它发出汇编。顺便说一句,x86 上的水平与与水平相加一样简单,对于非布尔向量,您不能只将向量提取到整数位图。另外顺便说一句,NEON 没有 pmovmskb 的等价物,因此将布尔向量转换为整数位图需要更多的工作。 而不是res += a[i] &lt; b[i]; 我认为最好使用res &amp;= a[i] &lt; b[i]; 来获得布尔结果而不是总和 彼得,我完全同意,但问题是关于编写编译器可以自动矢量化的代码。我写的是我能想到的最接近的,没有经过大量测试:( Lưu Vĩnh Phúc,&amp;= 将不起作用。也许你的意思是|=?正如我在回答中提到的,我决定改用+= 是有原因的,但|= 会起作用……它可能会慢一点。

以上是关于如何编写编译器可以针对 SIMD 比较优化的代码? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

g++ -O2 错误地优化了 SIMD 变量分配

手动 SIMD 代码的可负担性 [关闭]

使用 ARM SIMD 指令优化掩码功能

如何在 Visual Studio 2015(用于 C++)中仅禁用 SIMD 自动矢量化优化?

如何使用 SIMD 比较两个字符向量并将结果存储为浮点数?

演示代码在禁用优化的情况下未能显示 SIMD 速度快 4 倍