最初调查#pragma omp simd
指令的影响时,我遇到了一个与简单for循环向量化相关的我无法解释的行为。以下代码示例可以在这个惊人的编译器探测器上进行测试,只需应用-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的倍数。这背后是否有原因?
顺带一提,使用#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];
}
}
如果有人知道循环向量化为什么如此依赖索引类型,我会很好奇想要了解更多!
编辑2,感谢Mark Lakata提醒,需要使用O3。