Vit*_*meo 14 c++ optimization performance gcc auto-vectorization
考虑修复的这个最小实现vector<int>:
constexpr std::size_t capacity = 1000;
struct vec
{
int values[capacity];
std::size_t _size = 0;
std::size_t size() const noexcept
{
return _size;
}
void push(int x)
{
values[size()] = x;
++_size;
}
};
Run Code Online (Sandbox Code Playgroud)
鉴于以下测试用例:
vec v;
for(std::size_t i{0}; i != capacity; ++i)
{
v.push(i);
}
asm volatile("" : : "g"(&v) : "memory");
Run Code Online (Sandbox Code Playgroud)
编译器生成非向量化程序集:godbolt.org上的实例
如果我做出以下任何改变......
values[size()] - > values[_size]
添加__attribute__((always_inline))到size()
...然后编译器生成矢量化程序集:godbolt.org上的实例
这是一个gcc bug吗?或者有一个原因可以解释为什么一个简单的访问器size()会阻止自动矢量化,除非always_inline明确添加?
您的示例中的循环针对 GCC < 7.1 进行矢量化,而不针对 GCC >= 7.1 进行矢量化。所以这里的行为似乎发生了一些变化。
我们可以通过在命令行中添加以下内容来查看编译器优化报告:-fopt-info-vec-all
对于海湾合作委员会 7.3:
<source>:24:29: note: === vect_pattern_recog ===
<source>:24:29: note: === vect_analyze_data_ref_accesses ===
<source>:24:29: note: not vectorized: complicated access pattern.
<source>:24:29: note: bad data access.
<source>:21:5: note: vectorized 0 loops in function.
Run Code Online (Sandbox Code Playgroud)
对于海湾合作委员会 6.3:
<source>:24:29: note: === vect_pattern_recog ===
<source>:24:29: note: === vect_analyze_data_ref_accesses ===
<source>:24:29: note: === vect_mark_stmts_to_be_vectorized ===
[...]
<source>:24:29: note: LOOP VECTORIZED
<source>:21:5: note: vectorized 1 loops in function.
Run Code Online (Sandbox Code Playgroud)
因此,GCC 7.x 决定不对循环进行矢量化,因为访问模式很复杂,这可能是(此时)非内联size()函数。强制内联或手动修复该问题。GCC 6.x 似乎自己就做到了这一点。然而,在这两种情况下,程序集确实看起来size()最终都是内联的,但可能只是在 GCC 7.x 中的矢量化步骤之后(这是我的猜测)。
我想知道为什么你把这asm volatile(...)一行放在最后 - 可能是为了防止编译器丢弃整个循环,因为它在这个测试用例中没有明显的效果。如果我们只返回相反的最后一个元素v,我们可以达到相同的效果,而不会对 的内存模型造成任何可能的副作用v。
<source>:24:29: note: === vect_pattern_recog ===
<source>:24:29: note: === vect_analyze_data_ref_accesses ===
<source>:24:29: note: not vectorized: complicated access pattern.
<source>:24:29: note: bad data access.
<source>:21:5: note: vectorized 0 loops in function.
Run Code Online (Sandbox Code Playgroud)
该代码现在使用 GCC 7.x 进行矢量化,就像它在 GCC 6.x 中所做的那样:
<source>:24:29: note: === vect_pattern_recog ===
<source>:24:29: note: === vect_analyze_data_ref_accesses ===
<source>:24:29: note: === vect_mark_stmts_to_be_vectorized ===
[...]
<source>:24:29: note: LOOP VECTORIZED
<source>:21:5: note: vectorized 1 loops in function.
Run Code Online (Sandbox Code Playgroud)
那么这里的结论是什么呢?
asm volatile最佳猜测:内联size()防止矢量化带来的混乱的副作用这是否是一个错误(可能是在 6.x 或 7.x 中,具体取决于构造所需的行为asm volatile())对 GCC 开发人员来说是一个问题。
另外:尝试在命令行中添加-mavx2or -mavx512f -mavx512cd(或等),具体取决于您的硬件,以获得超过 128 位的矢量化,即和寄存器。-march=nativexmmymmzmm