简单的getter / accessor防止矢量化 - gcc bug?

时间:2018-02-13 13:17:26

标签: c++ performance gcc optimization 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;
    }
};

鉴于以下测试用例:

vec v;
for(std::size_t i{0}; i != capacity; ++i) 
{
    v.push(i);
}

asm volatile("" : : "g"(&v) : "memory");

编译器生成非向量化程序集:live example on godbolt.org

godbolt screenshot - non vectorized

如果我做任何以下更改......

  • values[size()] - &gt; values[_size]

  • __attribute__((always_inline))添加到size()

...然后编译器生成矢量化程序集:live example on godbolt.org

godbolt screenshot - vectorized

这是一个gcc错误吗?或者,除非明确添加size(),否则always_inline这样的简单访问器会阻止自动矢量化?

2 个答案:

答案 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.

那么这里的结论是什么?

  • GCC 7.1改变了一些内容
  • 最佳猜测:asm volatile混乱的副作用,内联size()阻止矢量化

这是否是一个错误 - 可能是6.x或7.x,取决于asm volatile()构造所需的行为 - 对于GCC开发人员来说是一个问题。

另外:尝试将-mavx2-mavx512f -mavx512cd(或-march=native等)添加到命令行,具体取决于您的硬件,以获得超过128位xmm的矢量化,即ymmzmm,注册。

答案 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移动指令

尝试一下:https://godbolt.org/g/NXgF4g