g++ -O2 错误地优化了 SIMD 变量分配

Posted

技术标签:

【中文标题】g++ -O2 错误地优化了 SIMD 变量分配【英文标题】:g++ -O2 incorrectly optimize out SIMD variable assignment 【发布时间】:2014-10-09 09:03:23 【问题描述】:

我正在使用英特尔 AVX2 指令编写程序。我在我的程序中发现了一个错误,它只出现在优化级别 -O2 或更高级别(使用 -O1 很好)。经过大量调试后,我缩小了错误区域。现在该错误似乎是由于编译器错误地优化了 __m256i 变量的简单复制分配造成的。

考虑以下代码 sn-p。 Foo 是一个模板函数。我用CMP = kLess, OPT=kSet 测试。我知道优化器可能会优化开关。它甚至可以优化变量y

有问题的线路是y = m_lt;。当使用 -O2 编译时,这行似乎被忽略了。然后y 没有得到正确的值,程序生成错误的结果。但是,使用 -O1 时程序是正确的。

为了验证我的判断,我将y = m_lt; 替换为两个备选方案:

y = avx_or(m_lt, avx_zero());m_lt 和一个全 0 的向量进行按位或运算

y = _mm256_load_si256(&m_lt);使用SIMD加载指令从m_lt的地址加载数据。

两者在语义上都应该等同于y = m_lt; 我的意图是通过添加一些功能来防止一些优化。该程序在所有优化级别下都可以正确使用这两个替换。所以问题很奇怪。据我所知,直接分配 SIMD 变量绝对没问题(我以前用过很多)。会不会是编译器的问题?

typedef __m256i AvxUnit;

template <Comparator CMP, Bitwise OPT>
void Foo()
    AvxUnit m_lt;
    //...

assert(!avx_iszero(m_lt));   //always pass

AvxUnit y;

switch(CMP)
    case Comparator::kEqual:
        y = m_eq;
        break;
    case Comparator::kInequal:
        y = avx_not(m_eq);
        break;
    case Comparator::kLess:
        y = m_lt;   //**********Bug?*************
        //y = avx_or(m_lt, avx_zero());   //Replace with this line is good.
        //y = _mm256_load_si256(&m_lt);   //Replace with this line is good too.
        break;
    case Comparator::kGreater:
        y = m_gt;
        break;
    case Comparator::kLessEqual:
        y = avx_or(m_lt, m_eq);
        break;
    case Comparator::kGreaterEqual:
        y = avx_or(m_gt, m_eq);
        break;


switch(OPT)
    case Bitwise::kSet:
        break;
    case Bitwise::kAnd:
        y = avx_and(y, bvblock->GetAvxUnit(bv_word_id));
        break;
    case Bitwise::kOr:
        y = avx_or(y, bvblock->GetAvxUnit(bv_word_id));
        break;


assert(!avx_iszero(y));   //pass with -O1, fail with -O2 or higher

bvblock->SetAvxUnit(y, bv_word_id);
//...

【问题讨论】:

也许是一个旁注,但y = avx_or(m_lt, avx_ones()); 真的能让事情正常运行吗?它应该给出所有的值...? @JoachimIsaksson 哦,抱歉,这是一个错误。我已经更正了。 内在函数不会比直接赋值更快吗?并不是说您没有遇到错误,但实际解决它可能会更快。 如果您认为存在编译器错误,请首先生成一个 SSCCE,如果执行缩减没有指出您的代码有问题,请将其发布到 gcc 的 bugzilla。这几乎是推动事情发展的唯一途径。 这是哪个版本? 【参考方案1】:

编译器放弃赋值的原因可能是它认为那行代码是dead code。所以你的CMP 不太可能是Comparator::kLess

您尝试作为解决方法的分配可以使用__asm__ volatile 语句实现,并且它们无法优化。

m_lt 声明为 volatile 可能不会对您的性能产​​生很大影响,但修复它是一种肮脏的技巧。我会更多地查看CMP 变量,看看它是否也可以采用kLess 值。

【讨论】:

感谢您的提醒。但关键是我确实希望编译器对其进行优化,但当然不会失去正确性。 您可以在“m_lt”变量上尝试 volatile。 y 变量仍将被优化。但是请尝试这两种情况,其中 y 也是易变的。我不希望代码从这种优化中得到很多。最大的性能提升是因为您使用 SIMD。

以上是关于g++ -O2 错误地优化了 SIMD 变量分配的主要内容,如果未能解决你的问题,请参考以下文章

移动分配比复制分配慢——错误、功能或未指定?

Mac 上的 G++ 链接时优化 - 编译器/链接器错误?

SIMD和动态内存分配[重复]

动态分配 SIMD 向量数组是不是安全?

64 位特定 simd 内在

使用结果浮点数时的 SSE SIMD 分段错误