问题描述
我遇到了一个奇怪的问题,cpu 认为我正在修改当前执行的代码,并反复触发 self-modifying code (SMC) machine clears。
我的(简化的)程序执行以下操作:
... 100'000'000 次迭代。
main.c:
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
extern void smc(void *bufferPtr,void *bufferEndPtr);
int main()
{
const int BUFFER_LENGTH = 4096;
void *bufferPtr = mmap(0,BUFFER_LENGTH,PROT_READ | PROT_WRITE | PROT_EXEC,MAP_ANONYMOUS | MAP_PRIVATE,0);
void *bufferEndPtr = bufferPtr + BUFFER_LENGTH;
printf("Instruction block buffer: %p,%s\n",bufferPtr,strerror(errno));
smc(bufferPtr,bufferEndPtr);
return 0;
}
smc.asm:
[section .text]
align 64
payload:
ret
%define BUFFER_STEP 64
align 64
[global smc]
; rdi: bufferPtr
; rsi: bufferEndPtr
smc:
push r10
push r11
push r12
mov rax,100_000_000
mov r10,rdi ; r10 points to begin of buffer
mov r11,rdi ; r11 points to current buffer position
mov r12,rsi ; r12 points to end of buffer
.loop:
; Done?
dec rax
je .end
mov rcx,64
mov rdi,r11
lea rsi,[rel payload]
; Store
rep movsb
; Call
call r11
; Move buffer pointer
lea r11,[r11 + BUFFER_STEP]
cmp r11,r12
jb .next
mov r11,r10
.next:
jmp .loop
.end:
pop r12
pop r11
pop r10
ret
编译:
nasm smc.asm -f elf64 -o smc.o
gcc -c main.c -O2 -o main.o
gcc main.o smc.o -o prog
我使用
测量程序的执行时间和MACHINE_CLEARS.SMC
performance counter
sudo perf stat -e r04c3 ./prog
在英特尔酷睿 i7-7567U 上的结果:
BUFFER_LENGTH (字节) |
BUFFER_STEP (字节) |
MACHINE_CLEARS.SMC |
执行时间(秒) |
---|---|---|---|
1 x 4K | 0 | 199'999'982 | 14.53 |
1 x 4K | 64 | 199'999'740 | 14.91 |
256 x 4K | 2048 | 105'550'699 | 7.89 |
256 x 4K | 4096 | 130'573'069 | 9.83 |
尽管我正在移动存储目的地(每次写入不同的位置),但我仍然清除了数百万个 SMC 机器,这导致了巨大的性能损失。
在 store 之前/之后添加各种围栏和/或序列化指令不会产生任何显着的改进。请注意,虽然移位在一定程度上减少了机器清除的次数,但也会导致 call
指令处出现大量分支目标错误预测。
当我使用 4K 缓冲区、0 字节步长、mfence
存储后和 call payload
而不是 call r11
运行相同的程序时,它只需要大约 1.74 秒,即考虑到已执行指令的总数。
是什么导致了如此大量的机器清除,我该如何解决?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)