如何使优化器正确地将变量放入寄存器中

How do I make the optimizer to properly put variables into registers?

本文关键字:寄存器 变量 何使 优化 正确地      更新时间:2023-10-16

>我有一个简单的函数,它通过引用接受两个变量:

void foo(int*& it2,
         bit_reader<big_endian_tag>& reader2)
{
    for(/* ... */)
    {
        *it2++ = boo(reader2.next());
        // it2++ => 0x14001d890 add qword ptr [r12], 0x4
    }
}

这里的问题是,对于it2reader2优化器使计算机在循环期间写入内存而不是寄存器。

但是,以下代码在循环期间将变量正确放入寄存器中,但在循环之前和之后以不必要的副本的形式产生额外的开销:

void foo2(int*& it2,
         bit_reader<big_endian_tag>& reader2)
{
    auto reader = reader2;
    auto it     = it2;
    for(/* ... */)
    {
        *it++ = boo(reader.next());
        // it++ => 0x14001d890 add r15, 0x4
    }
    reader2 = reader;
    it2 = it;
}

例如

如何使第一个示例生成与第二个示例相同的代码,但没有额外的副本?

问题是编译器无法证明it2函数内没有变化。(嗯,它可以,但这远远超出了普通C++编译器的预期功能。

它如何知道boo(reader2.next());不会更改值?考虑:

int* i = 0;
struct foo
{
    int myInt;
    int blah() { i = &myInt; return 5; }
};
void bar(int*& ptr, const foo& f)
{
    *ptr = f.blah(); // changes value of ptr!
}
int otherInt;
i = &otherInt;
bar(i, foo());

这不会为otherInt分配任何内容,而在转换后,它将:

void bar(int*& ptr, const foo& f)
{
    int* ptrCopy = ptr;
    *ptrCopy = f.blah(); // changes ptr, but not ptrCopy
}

因此,除非编译器能够证明行为相同,否则它无法进行优化。

C99 用 restrict 关键字解决了这个问题,但C++没有等效项。不过,大多数C++编译器中都存在扩展,例如 __restrict____restrict .

要以标准C++执行此操作,您只需明确并自己制作副本

即可

好吧,你不能。

通过非常量引用传递参数时,会要求编译器更新原始变量。因此,它必须将新值写入内存。

这一切都是为了优化"内存层次结构",直接在寄存器上完成计算是最快的,这就是为什么你真的想从内存中获取东西并将其复制到寄存器中,然后再计算任何内容,然后将结果复制回您需要的内存位置。通过直接在寄存器上计算所获得的性能通常会抵消在寄存器中加载和保存内存的成本。

你如何确保你把东西从内存中获取到寄存器中?例如

size_t size;
double* arr;
for (int i = 0; i < size - 1; ++i) {
    double a = arr[i];     // copy to register
    double b = arr[i + 1]; // copy to register
    b = a*b + b;           // make sure flop computation is done in registers
    arr[i] = b;            // copy back to memory
}