【发布时间】:2016-11-18 11:14:58
【问题描述】:
最初调查#pragma omp simd 指令的效果时,我遇到了一个我无法解释的行为,它与简单for 循环的向量化有关。下面的代码示例可以在这个很棒的compiler explorer 上进行测试,前提是应用了 -O3 指令并且我们使用的是 x86 架构。
谁能解释一下以下观察背后的逻辑?
#include <stdint.h>
void test(uint8_t* out, uint8_t const* in, uint32_t length)
{
unsigned const l1 = (length * 32)/32; // This is vectorized
unsigned const l2 = (length / 32)*32; // This is not vectorized
unsigned const l3 = (length << 5)>>5; // This is vectorized
unsigned const l4 = (length >> 5)<<5; // This is not vectorized
unsigned const l5 = length -length%32; // This is not vectorized
unsigned const l6 = length & ~(32 -1); // This is not vectorized
for (unsigned i = 0; i<l1 /*pick your choice*/; ++i)
{
out[i] = in[i*2];
}
}
令我困惑的是 l1 和 l3 都生成向量化代码,尽管不能保证是 32 的倍数。所有其他长度不生成向量化代码,但应该是 32 的倍数32. 这背后有什么原因吗?
顺便说一句,使用 #pragma omp simd 指令实际上并没有改变任何东西。
编辑:经过进一步调查,当索引类型为 size_t 时,行为差异消失了(甚至不需要边界操作),这意味着这会生成矢量化代码:
#include <stdint.h>
#include <string>
void test(uint8_t* out, uint8_t const* in, size_t length)
{
for (size_t i = 0; i<length; ++i)
{
out[i] = in[i*2];
}
}
如果有人知道为什么循环向量化如此依赖于索引类型,我很想知道更多!
Edit2,感谢 Mark Lakata,实际上需要 O3
【问题讨论】:
-
在这个问题的扩展中,Clang 可以看到完全相同的行为,所以我猜它有一些逻辑。
-
似乎编译器害怕索引可能会因为它而换行并放弃:-(
-
已经向我解释了类型依赖性,与溢出风险有关(这会阻止矢量化)。允许无符号溢出,而不允许有符号溢出,这就解释了最后一点。使用无符号并丢弃第一位(有效消除溢出风险)允许矢量化,GCC 非常聪明:godbolt.org/g/SsVZ2r
-
@BenjaminLefaudeux 示例中没有签名类型。
i*2始终未签名。i<length也是如此。 -
@BenjaminLefaudeux 请考虑接受明确的答案,我认为到 2501 年。
标签: c++ c gcc vector auto-vectorization