0

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

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

越野车线是y = m_lt;. 当使用 -O2 编译时,这一行似乎被忽略了。然后y没有得到正确的值,程序产生错误的结果。但是,该程序使用 -O1 是正确的。

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

y = avx_or(m_lt, avx_zero());采用按位 ORm_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);
//...
}
4

1 回答 1

1

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

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

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

于 2014-10-09T09:06:39.000 回答