ARM允许重新排序加载后续存储,以便以下伪代码:
// CPU 0 | // CPU 1
temp0 = x; | temp1 = y;
y = 1; | x = 1;
可以导致temp0 == temp1 == 1(并且,这在实践中也是可观察到的).我无法理解这是怎么发生的; 似乎有序提交会阻止它(这是我的理解,它存在于几乎所有的OOO处理器中).我的理由是"在提交之前,负载必须具有其值,它在存储之前提交,并且在提交之前,存储的值不会对其他处理器可见."
我猜我的一个假设肯定是错的,并且必须遵循下列之一:
说明不需要提交一路有序.稍后的存储可以安全地提交并在之前的加载之前变得可见,只要在存储提交核心时可以保证先前的加载(以及所有中间指令)不会触发异常,并且加载的地址是保证与商店不同.
负载可以在其值已知之前提交.我不知道如何实现这一点.
商店在提交之前可以显示.也许某个内存缓冲区允许将存储转发到另一个线程的加载,即使负载先前已加入?
还有别的吗?
有许多假设的微体系结构特征可以解释这种行为,但我最好的是那些实际存在于现代弱有序CPU中的那些.
我写了一个简单的循环:
int volatile value = 0;
void loop(int limit) {
for (int i = 0; i < limit; ++i) {
++value;
}
}
Run Code Online (Sandbox Code Playgroud)
我用gcc和clang(-O3 -fno-unroll-loops)进行了编译,得到了不同的输出。它们++value部分不同:
铛:
add dword ptr [rip + value], 1 # ++value
add edi, -1 # --limit
jne .LBB0_1 # if limit > 0 then continue looping
Run Code Online (Sandbox Code Playgroud)
gcc:
mov eax, DWORD PTR value[rip] # copy value to a register
add edx, 1 # ++i
add eax, 1 # increment a copy of value …Run Code Online (Sandbox Code Playgroud) 我有一个执行指针追踪的程序,我正在尝试尽可能地优化指针追踪循环。我注意到perf record检测到函数中大约 20% 的执行时间myFunction()用于执行跳转指令(用于在读取特定值后退出循环)。
需要注意的一些事项:
__builtin_expect以避免分支预测错误的成本没有显着影响perf record 有以下输出:
Samples: 153K of event 'cycles', 10000 Hz, Event count (approx.): 35559166926
myFunction /tmp/foobar [Percent: local hits]
Percent? endbr64
...
80.09 ?20: mov (%rdx,%rbx,1),%ebx
0.07 ? add $0x1,%rax
? cmp $0xffffffff,%ebx
19.84 ? ? jne 20
...
Run Code Online (Sandbox Code Playgroud)
我希望在这个循环中花费的大部分周期都用于从内存中读取值,这是由 perf 确认的。我还希望剩余的周期在执行循环中剩余的指令时会有些均匀地花费。相反, perf 报告的是剩余周期的很大一部分用于执行跳转。
我怀疑通过了解用于执行这些指令的微操作,我可以更好地了解这些成本,但我对从哪里开始有点迷茫。
我很难解释英特尔性能事件报告。
\n考虑以下主要读/写内存的简单程序:
\n#include <stdint.h>\n#include <stdio.h>\n\nvolatile uint32_t a;\nvolatile uint32_t b;\n\nint main() {\n printf("&a=%p\\n&b=%p\\n", &a, &b);\n for(size_t i = 0; i < 1000000000LL; i++) {\n a ^= (uint32_t) i;\n b += (uint32_t) i;\n b ^= a;\n }\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n我用gcc -O2以下命令编译它并运行perf:
#include <stdint.h>\n#include <stdio.h>\n\nvolatile uint32_t a;\nvolatile uint32_t b;\n\nint main() {\n printf("&a=%p\\n&b=%p\\n", &a, &b);\n for(size_t i = 0; i < 1000000000LL; i++) {\n a ^= (uint32_t) i;\n b += (uint32_t) i;\n b ^= a;\n }\n …Run Code Online (Sandbox Code Playgroud)