内联类方法的寄存器优化不足

时间:2017-01-23 10:02:49

标签: c++ optimization visual-studio-2015

为什么编译器不会在下面显示的代码示例中优化来自Iterator::Incr的内联代码的寄存器用法? (Visual Studio 2015,/ O2优化设置)

当我使用Iterator的引用来使用本文底部显示的C ++代码时,Iterator::Incr内部的方法Interpolator::InterpolateFast 被内联。但是,生成的汇编代码显示了对Iterator::_rem内存访问,尽管这个类成员变量可以放在寄存器内部,以提高内循环效率。

$LL2@Interpolat:
  00020 41 01 19     add     DWORD PTR [r9], ebx   ; memory access to Iterator::_rem (slow)
  00023 4d 63 01     movsxd  r8, DWORD PTR [r9]
  ...
  0003e 75 e0        jne     SHORT $LL2@Interpolat

当我编译复制迭代器的快速版本时,汇编程序代码将Iterator::_rem放在处理器寄存器中,并且只使用一个内存访问数组元素本身。

$LL2@Interpolat:
; 699  :        _rem += _incr;
  00011 45 03 c2     add     r8d, r10d    ; Iterator::_rem placed inside registers (fast)
...

当使用对Iterator类的引用时,似乎编译器会假定该类被修改或访问InterpolateFast(例如,通过并发线程),从而避免寄存器优化。

如何在不复制迭代器的情况下使用处理器寄存器优化内联方法?

typedef unsigned int BYTE;

class Iterator
{
public:
    Iterator() {}
    Iterator (const Iterator& it) :
        _rem (it._rem), _incr (it._incr) {}

    inline int Incr (const BYTE* &pSrc)
    {
        _rem += _incr;
        pSrc += _rem >> 16; 
        return _rem;
    }

private:
    int _rem;
    int _incr;
    friend class Interpolator;
};

class Interpolator
{
public:
    Interpolator (BYTE* p) : _p (p) {}
    int InterpolateFast (int len)
    {
        int sum = 0;
        const BYTE *p = _p;
        Iterator& it (_it);   // slow version, memory accesses to it._rem
        // Iterator it (_it); // fast version, registers optimized
        while (len--)
        {
            int rem = it.Incr (p);
            sum += p[0] * rem;
        }
        return sum;
    }

private:
    Iterator _it;
    const BYTE*    _p;
};

int main()
{
    BYTE arr[1000];
    Interpolator ip (arr);
    volatile int sum = ip.InterpolateFast (1000);
    return 0;
}

(请注意,此帖子的代码已经简化,并且没有有意义的功能。)

1 个答案:

答案 0 :(得分:1)

问题是,在函数结束时,_it必须已被Incr的调用修改。使用该副本,唯一需要更新的对象将被销毁,因此更新不在外部可见。

显然编译器可以在循环结束时更新内存,但是在出现早期返回或异常时这很棘手。你和我可以看到这里没有适用,但编译器可能已经丢失了。

还有一个问题是,如果循环内部有更复杂的调用,编译器将需要确保在循环内更新_it

最简单的工作是:

    Iterator it (_it); // fast version, registers optimized
    while (len--)
    {
        int rem = it.Incr (p);
        sum += p[0] * rem;
    }
    _it = it;  // ****** Added line
    return sum;

顺便提一下,你有时间表明内存访问速度慢吗?在第一次迭代之后,它将在L1缓存中,这比寄存器慢得多。 (This answer建议可能额外增加3-5个周期,这可能与其他东西重叠。)