【问题标题】:Crash using movapd with aligned memory使用对齐内存的 movapd 崩溃
【发布时间】:2019-08-11 11:05:03
【问题描述】:

我在 Windows 上使用 64 位 NASM 为 malloc 分配了一块内存。当我使用 movapd (移动两个对齐的打包双精度浮点值)时,我的程序崩溃,但我可以使用 movupd (移动两个未对齐的打包双精度浮点点值)。

我更改为_aligned_malloc 指定alignment=16,它可以分配内存,但是当我使用movapd 时仍然出现错误。在执行指令之前,我的调试器显示 R8=0xB0FC78、R15=0x12FC0050、RCX=0x6D40050 和 RDX = 0x10010050。 RCXRDX 是指向两个内存块的指针。 R8RCX 是跨块的计数器

这行得通:

movupd xmm0,[rdx+r8]
movupd [r15+rcx],xmm0

这会崩溃:

movapd xmm0,[rdx+r8]
movapd [r15+rcx],xmm0

奇怪的是,使用 _aligned_malloc 的程序比使用 malloc 的内存要慢。

我的代码是用 NASM(汇编语言)编写的,但我认为语言并不重要。

为什么即使使用_aligned_malloc 分配的内存,我也不能使用 movapd

【问题讨论】:

  • 就在这些指令执行之前,调试器所说的 rdx、r8、r15 和 rcx 中的值是多少?我只能猜测,当计算 rdx+r8 和/或 r15+rcx 时,结果地址没有正确对齐。
  • 我现在正在获取该信息。我会在几分钟后回来。
  • 最终有效地址不能对齐。除了对齐之外,没有任何设置会使movapd 崩溃,但movupd 会成功。使用您的调试器。您是否忘记按sizeof(double) = 8 或其他方式缩放数组偏移量?
  • 在 HEX 中更易于阅读。 r8=B0FC78, r15=12FC0050 rcx=6​​D40050 rdx=10010050
  • r8 的最后一个半字节(最后 4 位)没有 0,因此当添加到已对齐的 rdx 时,它会破坏对齐。(rdx 的低半字节为 0)

标签: windows assembly x86-64 simd memory-alignment


【解决方案1】:

这是在这个特殊情况下这个问题的答案。多核处理后,我有四个小缓冲区组合成一个大缓冲区。我正在使用 SIMD 来加快这个过程。

下面是传入最后一步(合并)的寄存器值,除以16。

7024645 0 714474.5 19906565 0 线程 1

10285061 0 728618 19906565 714474.5 线程 2

13414405 0 726064.5 19906565 1443092.5 线程 3

16781317 0 724936 19906565 2169157 线程 4

第三和第五列显示的值不能被 16 整除,所以我不能使用对齐访问。第三列是该线程的小缓冲区的大小。 “未对齐”的原因是因为程序为每个段返回不相等数量的四字,这意味着最终输出可能会或可能不会以 16 的偶数倍数结尾——举个简单的例子,如果我有三个四字( 24 字节),不能被 16 整除。

根据这篇博文,内存对齐不会显着影响最新处理器的性能:https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/

我会对这方面的任何 cmets 感兴趣。谢谢您的帮助。

【讨论】:

  • 您链接的博客正在测试标量整数代码。确实,在 Nehalem / Bulldozer 及更高版本上,高速缓存行拆分的惩罚很低,但能够将内存操作数折叠到 ALU 指令中(如addpd xmm0, [rdi])可以节省微指令而不是单独的movupd除非您有 AVX,否则需要可能未对齐的数据。
  • 我根本不明白这是如何回答这个问题的。 xmm0 / memory 中的双精度数字不必被 16 整除,它们的地址可以。地址是整数,所以很明显714474.5 不是指针或地址。如果你有 24 个字节的数据,你会使用movapd 作为前 16 个字节,movsd 作为最后 8 个字节。
  • 当我们将四个缓冲区合二为一时,大缓冲区的起始字节将是前一个起始字节加上前一个小缓冲区的长度。如果前面的小缓冲区不是以 16 的偶数倍结束,那么大缓冲区的起始字节将错位(就像这里一样)。指针地址 714474.5 是 11431592 除以 16。
  • 我当然想使用对齐访问,但我不知道如何在这里做到这一点。
  • 哦,我明白了。那很不清楚。你谈到有 4 个小缓冲区,但是你有 5 列。每个线程实际上有 5 个缓冲区,其中一些是 NULL?你展示了以 10 为底的分数,这对于指针来说又是完全奇怪的。你没有说你在说哪个寄存器。
猜你喜欢
  • 2019-07-02
  • 1970-01-01
  • 2019-03-24
  • 2012-01-28
  • 2012-05-12
  • 1970-01-01
  • 2022-11-02
  • 2012-02-22
  • 1970-01-01
相关资源
最近更新 更多