您示例中的循环针对 GCC < 7.1 进行了矢量化,而不针对 GCC >= 7.1 进行了矢量化。所以这里的行为似乎发生了一些变化。
我们可以通过在命令行中添加来查看编译器优化报告:-fopt-info-vec-all
对于 GCC 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.
对于 GCC 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.
因此 GCC 7.x 决定不对循环进行矢量化,因为访问模式很复杂,这可能是(当时)非内联size()
函数。强制内联,或手动进行修复。GCC 6.x 似乎可以自己做到这一点。但是,在这两种情况下,程序集看起来确实size()
是最终内联的,但可能只是在 GCC 7.x 中的矢量化步骤之后(这是我的猜测)。
我想知道为什么你把这asm volatile(...)
行放在最后——可能是为了防止编译器丢弃整个循环,因为它在这个测试用例中没有明显的效果。如果我们只返回 的最后一个元素,我们可以达到相同的效果,v
而不会对 的内存模型造成任何可能的副作用v
。
return v.values[capacity - 1];
代码现在用 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.
那么这里的结论是什么?
- GCC 7.1 发生了一些变化
- 最佳猜测:防止矢量化
asm volatile
的内联混乱的副作用size()
这是否是一个错误 - 可能在 6.x 或 7.x 中,具体取决于构造所需的行为asm volatile()
- 对于 GCC 开发人员来说是一个问题。
另外:根据您的硬件,尝试在命令行中添加-mavx2
或-mavx512f -mavx512cd
(或等),以获得超过 128 位的矢量化,即和寄存器。-march=native
xmm
ymm
zmm