如果预取无效地址会怎样?
What happens if an invalid address is prefetched?
简单的 MWE:
int* ptr = (int*)malloc(64 * sizeof(int));
_mm_prefetch((const char*)(ptr + 64), _MM_HINT_0);
- 这是定义的还是未定义的行为?
- 这可以发出信号并中止程序吗运行?
我问是因为我可以在编译器生成的代码中看到这样的预取,其中在循环预取中完成而不检查地址(存储在 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>
首先,编译器做这件事和你做这件事在理论上是非常不同的事情。仅仅因为它看起来是等同的并不能使它如此,编译器被允许使用任何有效的肮脏的 hack,无论它们是可表达的还是在完全标准的 C 中定义的。
当然预取不会产生信号*,如果它产生了几乎没有用。但是,对于某些无效指针,它可能会非常慢,具体取决于它们是否触发 TLB 未命中。所以编译器可以安全地使用它,但它不应该无差别地使用它。
现在使用指针算法来创建越界指针(除了刚刚结束的指针)在理论上是 UB,但是当应用于指针时,它是一种无论如何都会起作用的 UB(对于平面内存,它只是一个此外,它可能失败的唯一方法是编译器竭尽全力检测它,这意味着它必须推断动态大小)。显然,上述情况必须得到声称支持 SSE 内在函数的编译器的支持,否则您无法合理地使用预取,正如这个答案所证明的那样(并且他们必须在标准之上做出更多额外保证)。
* 来自手册:
The PREFETCHh instruction is merely a hint and does not affect program behavior.
信号会影响程序行为,因此无法生成。
简单的 MWE:
int* ptr = (int*)malloc(64 * sizeof(int));
_mm_prefetch((const char*)(ptr + 64), _MM_HINT_0);
- 这是定义的还是未定义的行为?
- 这可以发出信号并中止程序吗运行?
我问是因为我可以在编译器生成的代码中看到这样的预取,其中在循环预取中完成而不检查地址(存储在 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>
首先,编译器做这件事和你做这件事在理论上是非常不同的事情。仅仅因为它看起来是等同的并不能使它如此,编译器被允许使用任何有效的肮脏的 hack,无论它们是可表达的还是在完全标准的 C 中定义的。
当然预取不会产生信号*,如果它产生了几乎没有用。但是,对于某些无效指针,它可能会非常慢,具体取决于它们是否触发 TLB 未命中。所以编译器可以安全地使用它,但它不应该无差别地使用它。
现在使用指针算法来创建越界指针(除了刚刚结束的指针)在理论上是 UB,但是当应用于指针时,它是一种无论如何都会起作用的 UB(对于平面内存,它只是一个此外,它可能失败的唯一方法是编译器竭尽全力检测它,这意味着它必须推断动态大小)。显然,上述情况必须得到声称支持 SSE 内在函数的编译器的支持,否则您无法合理地使用预取,正如这个答案所证明的那样(并且他们必须在标准之上做出更多额外保证)。
* 来自手册:
The PREFETCHh instruction is merely a hint and does not affect program behavior.
信号会影响程序行为,因此无法生成。