Linux 性能启动开销:为什么执行MOV+;SYS_exit有这么多暂停循环(和指令)?
我试图理解如何衡量性能,并决定编写一个非常简单的程序:Linux 性能启动开销:为什么执行MOV+;SYS_exit有这么多暂停循环(和指令)?,linux,performance,assembly,x86-64,perf,Linux,Performance,Assembly,X86 64,Perf,我试图理解如何衡量性能,并决定编写一个非常简单的程序: section .text global _start _start: mov rax, 60 syscall 我用perf stat./bin运行了这个程序,令我惊讶的是停止循环的前端太高了 0.038132 task-clock (msec) # 0.148 CPUs utilized 0 context-sw
section .text
global _start
_start:
mov rax, 60
syscall
我用perf stat./bin运行了这个程序,令我惊讶的是停止循环的前端太高了
0.038132 task-clock (msec) # 0.148 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
2 page-faults # 0.052 M/sec
107,386 cycles # 2.816 GHz
81,229 stalled-cycles-frontend # 75.64% frontend cycles idle
47,654 instructions # 0.44 insn per cycle
# 1.70 stalled cycles per insn
8,601 branches # 225.559 M/sec
929 branch-misses # 10.80% of all branches
0.000256994 seconds time elapsed
据我所知,暂停周期前端意味着CPU前端必须等待某些操作(如总线事务)的结果完成
那么,在这种最简单的情况下,是什么导致CPU前端等待了大部分时间呢
和2页错误?为什么?我没有读取内存页。页面错误包括代码页
perf stat
包括启动开销
IDK了解perf
如何开始计数的详细信息,但它可能必须在内核模式下对性能计数器进行编程,因此它们在CPU切换回用户模式时计数(暂停许多周期,特别是在具有使TLB失效的熔毁防御的内核上)
我想大部分记录的47654
指令都是内核代码。可能包括页面错误处理程序
我猜您的进程永远不会进入用户->内核->用户,整个过程是内核->用户->内核(启动,syscall
调用sys\u exit
,然后再也不会返回到用户空间),所以无论如何TLB都不会很热,除了在sys\u exit
系统调用后在内核内运行时。无论如何,TLB未命中并不是页面错误,但这可以解释很多停滞周期
用户->内核转换本身解释了大约150个暂停周期,顺便说一句。syscall
比缓存未命中要快(除了它没有流水线,事实上会刷新整个流水线;即特权级别没有重命名。)你怎么不担心47654指令呢?:)不确定perf具体计算的是什么,但很可能它执行的其他任何东西(内核代码?)也会导致暂停。如果您复制/粘贴真实代码(和构建指令),它本来就不可能存在。始终这样做,而不是重新键入代码。如果您正在编写代码,并且没有在任何地方对其进行过测试,则只能在中键入代码。@PeterCordes正是缺少的\u start:
标签使我认为正常初始化已执行。另外,对于libc链接是如何被抑制的问题也缺乏描述:如果您没有明确尝试这样做,那么要生成没有这些内容的可执行文件是非常困难的。无论如何,不管我的猜测是否正确,我都不会删除我的评论:链接非常好,其他读者可能也感兴趣。@cmaster:ld foo.o-o foo
在Linux上生成一个静态可执行文件。不,动态链接的原因不是对内核头的依赖(用户空间ABI是稳定的,您不需要不同的glibc来匹配您的内核)。原因是libc没有嵌入到每个二进制文件中,glibc错误修复也不需要重新编译所有文件。加上通常的记忆优势。使用gcc,要使用自定义的\u start
创建静态可执行文件,请使用gcc foo.o-nostlib-static
。或者将libc动态链接到您自己的\u start
,gcc foo.o-nostartfiles
(glibc init funcs仍在运行)@ CMASTER:GLUBC init函数运行在动态加载的C++库中运行构造函数的相同机制:<代码> LIBC。因此,6 < /COD>在“代码”>“全局代码”//COD>数组或类似的东西中有必要的init函数的地址,动态链接器在跳到如果gcc告诉链接器,则链接器仅“将代码插入…调用main
”(即CRT(C运行时)启动文件)。如果运行gcc-vfoo.c
,您将看到编译器、汇编程序和链接器命令行(ld
通过collect2
包装器调用,但您可以看到gcc
显式传递的crt0.o
),这是一个快速的部分答案;我希望看到一个答案,它能够准确地解释哪些指令(大概是内核指令)包含在perf stat
计算为启动/关闭开销的部分中。如果你能控制它;我认为x86性能计数器可以编程为仅在用户模式或内核+用户模式下计数。它统计了10519个事件。最上面是下面一行35.55%perf[kernel.kallsyms]
。这是否意味着在这种简单的情况下,性能计数TLB未命中开销非常重要?@St.Antario:显然是的。但你有47k指令的10k TLB失误?这听起来高得难以置信。听起来我们需要一个真正的答案来回答这个问题,而不仅仅是我的手在perf
上挥舞,包括一些启动开销。但是如果perf
启动开销太高。。。我们如何使用它编写微/纳米基准测试?甚至可能吗?@St.Antario:将代码放入一个至少运行50毫秒的循环中。我有一个testloop.asm
和一个assembly+link+perf stat
命令行,我可以很容易地找到它,它使用mov ebp,100000000
作为dec ebp/jnz
循环的循环计数器。如果循环中的代码超过几个快速UOP,我可以根据需要添加或删除零。使用perf stat-r4
,我获得了非常好的重复性和准确性,在0.1到1秒左右的运行时间内,预期结果在10k或100k的1部分之内。看见