最初调查#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];
}
}
令我困惑的是,尽管不能保证是 32 的倍数,但 l1 和 l3 都生成矢量化代码。所有其他长度都不会产生矢量化代码,但应该是 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