为什么编译器不会在下面显示的代码示例中优化来自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;
}
(请注意,此帖子的代码已经简化,并且没有有意义的功能。)
答案 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个周期,这可能与其他东西重叠。)