如果预取无效地址会发生什么?

问题描述 投票:0回答:1

简单的MWE:

int* ptr = (int*)malloc(64 * sizeof(int));
_mm_prefetch((const char*)(ptr + 64), _MM_HINT_0);
  1. 这是已定义的行为还是未定义的行为?
  2. 这可以发出信号并中止程序运行吗?

我这么问是因为我可以在编译器生成的代码中看到这样的预取,其中在循环内预取是在不检查地址(存储在

rbx
中)的情况下完成的:

400e73:       49 83 c5 40             add    r13,0x40
400e77:       62 f1 f9 08 28 03       vmovapd zmm0,ZMMWORD PTR [rbx]
400e7d:       4d 3b ec                cmp    r13,r12
400e80:       62 d1 f9 08 eb 4d ff    vporq  zmm1,zmm0,ZMMWORD PTR [r13-0x40]
400e87:       90                      nop
400e88:       62 d1 78 08 29 4d ff    vmovaps ZMMWORD PTR [r13-0x40],zmm1
400e8f:       72 03                   jb     400e94 <main+0x244>
400e91:       49 89 c5                mov    r13,rax
400e94:       62 f1 78 08 18 53 1d    vprefetch1 [rbx+0x740]
400e9b:       ff c1                   inc    ecx
400e9d:       62 f1 78 08 18 4b 02    vprefetch0 [rbx+0x80]
400ea4:       48 83 c3 40             add    rbx,0x40
400ea8:       81 f9 00 00 10 00       cmp    ecx,0x100000
400eae:       72 c3                   jb     400e73 <main+0x223>
c caching assembly disassembly prefetch
1个回答
5
投票

首先,编译器做的事情和你做的事情在理论上是非常不同的。仅仅因为它看起来等效并不意味着它如此,编译器可以使用任何有效的肮脏黑客,无论它们是可表达的还是在完全标准 C 中定义的。

当然,预取不会生成信号*,如果生成了,它几乎毫无用处。对于特别旧的 CPU 上的一些无效指针来说,它可能会非常慢(参见例如预取的问题),但那是一篇旧文章,现在似乎不再那么糟糕了,例如在 Intel Rocket Lake 预取上无效的指针没什么大不了的。即使它没有陷入性能陷阱,显式预取也不是免费的,也不一定有帮助(例如,自动预取涵盖了许多正常的访问模式)。因此编译器可以安全地使用它,但它不应该不加区别地将它用于所有事情。

现在使用指针算术来创建越界指针(除了刚刚超出末尾)在理论上是 UB,但是当应用于指针时,它是那种无论如何都会起作用的 UB(对于平面内存来说,它只是一个补充,唯一的如果编译器不遗余力地检测它,它可能会失败,这意味着它必须推理动态大小)。显然,上述情况必须得到声称支持 SSE 内在函数的编译器的支持,否则您将无法合理使用预取,如本答案所示(并且他们必须在标准之上做出更多额外保证)。

* 来自手册:

PREFETCHh 指令只是一个提示,不会影响程序行为。

信号会影响程序行为,因此无法生成。

© www.soinside.com 2019 - 2024. All rights reserved.