use*_*476 5 performance x86 x86-64 performancecounter perf
以下代码片段仅使用一条RET指令创建一个函数(fun).循环重复调用该函数并在返回后覆盖RET指令的内容.
#include <sys/mman.h>
#include<stdlib.h>
#include<unistd.h>
#include <string.h>
typedef void (*foo)();
#define RET (0xC3)
int main(){
// Allocate an executable page
char * ins = (char *) mmap(0, 4096, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, 0, 0);
// Just write a RET instruction
*ins = RET;
// make fun point to the function with just RET instruction
foo fun = (foo)(ins);
// Repeat 0xfffffff times
for(long i = 0; i < 0xfffffff; i++){
fun();
*ins = RET;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
X86 Broadwell机器上的Linux perf具有以下icache和iTLB统计信息:
perf stat -e L1-icache-load-miss -e iTLB-load-miss ./a.out
"./a.out"的效果统计信息统计信息:
805,516,067 L1-icache-load-misses
4,857 iTLB-load-misses
32.052301220 seconds time elapsed
Run Code Online (Sandbox Code Playgroud)
现在,查看相同的代码而不覆盖RET指令.
#include <sys/mman.h>
#include<stdlib.h>
#include<unistd.h>
#include <string.h>
typedef void (*foo)();
#define RET (0xC3)
int main(){
// Allocate an executable page
char * ins = (char *) mmap(0, 4096, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, 0, 0);
// Just write a RET instruction
*ins = RET;
// make fun point to the function with just RET instruction
foo fun = (foo)(ins);
// Repeat 0xfffffff times
for(long i = 0; i < 0xfffffff; i++){
fun();
// Commented *ins = RET;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是同一台机器上的性能统计数据.
perf stat -e L1-icache-load-miss -e iTLB-load-miss ./a.out
"./a.out"的效果统计信息统计信息:
11,738 L1-icache-load-misses
425 iTLB-load-misses
0.773433500 seconds time elapsed
Run Code Online (Sandbox Code Playgroud)
请注意,覆盖该指令会导致L1-icache-load-miss从11,738增加到805,516,067 - 这是一个多方面的增长.另外请注意,iTLB负载未命中率从425增加到4,857 - 与L1-icache-load-miss相比相当增长但却更少.运行时间从0.773433500秒增加到32.052301220秒 - 增长41倍!
目前还不清楚,如果指令占用空间很小,CPU应该导致i-cache未命中.两个示例中唯一的区别是指令被修改.当然,L1 iCache和dCache是分开的,是不是有办法将代码安装到iCache中,这样可以避免缓存i-cache未命中?
此外,为什么iTLB未命中增长10倍?
\n\n\n假设一级 iCache 和 dCache 是分开的,是否有办法将代码安装到 iCache 中,以避免缓存 i-cache 未命中?
\n
不。
\n\n如果您想修改代码 - 唯一可以采用的路径如下:
\n\n请注意,您还错过了 \xce\xbcOP 缓存。
\n\n图1对此进行了说明,我认为该图足够准确。
\n\n我怀疑 iTLB 未命中可能是由于定期 TLB 刷新造成的。如果没有修改,您不会受到 iTLB 未命中的影响,因为您的指令实际上来自 \xce\xbcOP 缓存。
\n\n如果他们不这样做,我不太确定。我认为 L1 指令缓存是虚拟寻址的,因此如果命中则无需访问 TLB。
\n\n1:不幸的是,该图像具有非常严格的版权,因此我避免突出显示路径/内联图像。
\n