【问题标题】:How exactly does alignment impact memory layout and the bahaviour of placement new?对齐究竟如何影响内存布局和放置新的行为?
【发布时间】:2019-06-17 15:45:00
【问题描述】:

我们阅读了很多关于对齐及其重要性的文章,例如对于放置 new 的使用,但我想知道 - 它究竟如何改变内存的布局?

显然,如果我们这样做了

char buffer[10];
std::cout << sizeof buffer;

alignas(int) char buffer[10];
std::cout << sizeof buffer;

我们得到相同的结果,即10

但行为不可能完全相同,不是吗?怎么区分的?我试图寻找答案并跑到godbolt,测试以下代码:

#include <memory>

int main() {
    alignas(int) char buffer[10];
    new (buffer) int;
}

在 GCC 8.2 和没有优化的情况下,会导致以下程序集:

operator new(unsigned long, void*):
    push    rbp
    mov     rbp, rsp
    mov     QWORD PTR [rbp-8], rdi
    mov     QWORD PTR [rbp-16], rsi
    mov     rax, QWORD PTR [rbp-16]
    pop     rbp
    ret
main:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    lea     rax, [rbp-12]
    mov     rsi, rax
    mov     edi, 4
    call    operator new(unsigned long, void*)
    mov     eax, 0
    leave
    ret

让我们通过删除alignas(int) 部分来稍微更改代码。现在,生成的程序集略有不同:

operator new(unsigned long, void*):
    push    rbp
    mov     rbp, rsp
    mov     QWORD PTR [rbp-8], rdi
    mov     QWORD PTR [rbp-16], rsi
    mov     rax, QWORD PTR [rbp-16]
    pop     rbp
    ret
main:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    lea     rax, [rbp-10]
    mov     rsi, rax
    mov     edi, 4
    call    operator new(unsigned long, void*)
    mov     eax, 0
    leave
    ret

值得注意的是,它的区别仅在于lea 指令,其中第二个参数是[rbp-10] 而不是[rbp-12],就像我们在alignas(int) 版本中那样。

请注意,我通常不懂汇编。我不能写汇编,但我能读懂它。据我了解,差异只是改变了内存地址的偏移量,它将保存我们的位置-newed int

但它实现了什么?为什么我们需要那个?假设我们有一个 buffer 数组的“通用”表示,如下所示:

[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]

现在,我假设,在放置-newing int有或没有对齐)之后,我们最终会得到这样的结果:

[x] [x] [x] [x] [ ] [ ] [ ] [ ] [ ] [ ]

其中x 表示int 的单个字节(我们假设sizeof(int) == 4)。

但我一定错过了什么。还有更多,我不知道是什么。通过将bufferint 匹配对齐,我们究竟能实现什么?如果我们如此对齐会发生什么?

【问题讨论】:

  • 当您在同一缓冲区中放置新的 char 并立即在其“之后”放置新的 int 时,就会出现问题。
  • @MooingDuck 所以假设我永远不会将我放置的类型 -new 混合到我的缓冲区中(假设我以非常简化的方式模仿 std::vector 的缓冲区方式),我可以在没有任何对齐的情况下做到这一点?
  • 不,对齐仍然是一个问题,在混合类型之前很难注意到问题。
  • 相关,但不完全回答这个问题:stackoverflow.com/questions/119123/…

标签: c++ memory-alignment placement-new memory-layout alignas


【解决方案1】:

在某些架构上,必须对齐类型才能使操作正常工作。例如,int 的地址可能需要是 4 的倍数。如果不是这样对齐,那么对内存中的整数进行操作的 CPU 指令将无法工作。

即使当数据没有很好地对齐时一切工作,对齐对于性能仍然很重要,因为它确保整数等不会跨越缓存边界。

当您将char 缓冲区与整数边界对齐时,它不会影响放置 new 的方式。它只是确保您可以使用placement new 将int 放在缓冲区的开头,而不会违反任何对齐约束。它通过确保缓冲区的地址是 4 个字节的倍数来做到这一点。

【讨论】:

    猜你喜欢
    • 2020-07-30
    • 2012-08-08
    • 1970-01-01
    • 2012-01-25
    • 2021-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多