什么是临时错位

What is a misaligned temporary?

本文关键字:错位 什么      更新时间:2023-10-16

当谈到const T&时,这个答案提到它可能会因为"临时性不一致"而变慢。什么是未对齐的临时性,我在代码中会在哪里遇到它?

答案是:

由于临时性不一致和间接成本,const T&的获取速度可能较慢。

然后两条评论:

即使没有由于错位而导致的性能损失,引用被实现为指针这一事实也需要将值存储在主存储器中,主存储器的速度可能比通过寄存器慢一个(一级缓存(到十个(页面故障(数量级。

这并不能解释什么是未对齐的临时地址。指针可以指向未对齐的地址,但这不能解释什么为未对齐的暂时地址。下一条注释未处理:

为什么临时仓库会不对齐?除非您偏离了未定义的行为,否则C++中的所有对象都是按定义对齐的。

在严格的C++中,任何对象都不能错位[basic.align]/1:

对象类型具有对齐要求(〔basic.basic〕,〔basic.compound〕(,这些要求对可以分配该类型对象的地址进行了限制。

因此,无论是否临时,对象都不能错位。也就是说,未对齐的对象在低级别代码中使用,但此类代码涉及非标准c++代码(例如,请参阅Linux内核doc/unlined memory access.txt(。您可以尝试通过以下方式将未对齐(不存在(的对象传递给函数:

void f(const int& x);
void g(long x){
f(*reinterpret_cast<const int*>(reinterpret_cast<const unsigned char*>(&x)+1));
}

在这样一个会导致未定义行为的代码中,编码器清楚地向我们表明他没有遵循标准,传递给fint对象不存在。所以我们在另一个领域,也许在这个领域可以将这个int对象称为临时对象。事实上确实存在这样的代码。

关于缓存未命中的第一个注释需要了解调用约定(如何在程序集级别传递参数(才能被理解。在所有ABI上,引用都是通过指向被引用对象的指针传递的。因此,引用的对象必须复制到内存中。最初存在于寄存器中的g的奇偶校验器x将被复制到将被传递给f的存储器位置处的存储器中。以下是gcc生产的g的AMD64汇编代码:

//the argument x of g is in register rdi.
g(long):
sub     rsp, 24
mov     QWORD PTR [rsp+8], rdi  //x is copied on to the stack
lea     rdi, [rsp+9]  //the misaligned pointer is stored in
//the first argument of f
call    f(int const&)
add     rsp, 24
ret

传递给f的参数存在于内存中,当后者的值将被访问时,该访问可能会产生缓存未命中,并且可能会产生比正确对齐时更多的缓存未命中(因为int对象可能分布在两条缓存线上((请参阅rsp+9(。因此,如果对象本来可以在寄存器上传递,则通过引用传递参数不是最佳的(也就是说,如果对象是普通的可复制对象,并且足够小——在窗口x86_64上为8字节,对于x86_64的Sys V abi为16字节(。如果物体错位,这是最糟糕的。

下面是当f按值取其参数时生成的更好的代码:

void f(int x);
void g(long x){
f(*reinterpret_cast<const int*>(reinterpret_cast<const unsigned char*>(&x)+1));
}

生成的组件是完美的:

g(long):
sal     rdi, 24   //no memory access just a right shift right and left shift
sar     rdi, 32
jmp     f(int)    //tail call a ret from f will jump directly
//to g caller!

没有理由期望临时工失调。由于间接性,对引用效率低于值的担忧是有效的,也许偶尔你甚至会发现对未对齐值的引用,但未对齐的临时引用是极不可能的。

注意:在这个问题发布之前,在网络上搜索"c++"错位临时"只有一个结果,这导致了404错误页面。