考虑固定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;
}
};
鉴于以下测试用例:
vec v;
for(std::size_t i{0}; i != capacity; ++i)
{
v.push(i);
}
asm volatile("" : : "g"(&v) : "memory");
编译器生成非向量化程序集:live example on godbolt.org
如果我做任何以下更改......
values[size()]
- &gt; values[_size]
将__attribute__((always_inline))
添加到size()
...然后编译器生成矢量化程序集:live example on godbolt.org
这是一个gcc错误吗?或者,除非明确添加size()
,否则always_inline
这样的简单访问器会阻止自动矢量化?
答案 0 :(得分:1)
示例中的循环是针对GCC进行矢量化的&lt; 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()
函数。强制内联,或手动修复它。海湾合作委员会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.
那么这里的结论是什么?
asm volatile
混乱的副作用,内联size()
阻止矢量化这是否是一个错误 - 可能是6.x或7.x,取决于asm volatile()
构造所需的行为 - 对于GCC开发人员来说是一个问题。
另外:尝试将-mavx2
或-mavx512f -mavx512cd
(或-march=native
等)添加到命令行,具体取决于您的硬件,以获得超过128位xmm
的矢量化,即ymm
和zmm
,注册。
答案 1 :(得分:0)
我可以缩小问题范围。
以双精度或单精度和优化标志-std = c ++ 11 -Ofast -march = native:
Clang with Version&gt; = 5.0.0生成带有zmm寄存器的AVX移动指令
4.9&lt; = Version&lt; = 6.3的Gcc产生带zmm寄存器的AVX移动指令
版本&gt; = 7.1.0的Gcc生成带有xmm寄存器的AVX移动指令