关于矢量化和循环大小的令人费解的GCC行为

Ben*_*eux 9 c c++ gcc vector auto-vectorization

最初调查#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];
    }
}
Run Code Online (Sandbox Code Playgroud)

令我困惑的是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];
    }
}
Run Code Online (Sandbox Code Playgroud)

如果有人知道为什么循环向量化如此依赖于索引类型那么,我很想知道更多!

Edit2,感谢Mark Lakata,实际上需要O3

250*_*501 4

问题很明显是数组索引中从1unsigned到1 的转换:size_tin[i*2];

如果您使用l1或 ,l3则 的计算i*2将始终适合类型size_t。这意味着该类型unsigned实际上表现为size_t.

但是,当您使用其他选项时,计算结果i*2可能不适合 size_t,因为值可能会换行并且必须进行转换。

如果您采用第一个示例,不选择选项 l1 或 l3,然后进行转换:

out[i] = in[( size_t )i*2];
Run Code Online (Sandbox Code Playgroud)

如果您转换整个表达式,编译器会优化:

out[i] = in[( size_t )(i*2)];
Run Code Online (Sandbox Code Playgroud)

事实并非如此。


1标准实际上并未指定索引中的类型必须是size_t,但从编译器的角度来看,这是一个逻辑步骤。