我试图理解如何衡量性能,并决定编写非常简单的程序:
section .text
global _start
_start:
mov rax, 60
syscall
我使用perf stat ./bin
运行了程序,让我惊讶的是stalled-cycles-frontend
太高了。
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
据我理解,stalled-cycles-frontend
表示 CPU 前端需要等待某些操作(例如总线事务)的结果才能完成。
那么在这种最简单的情况下,是什么导致 CPU 前端大部分时间等待呢?
还有 2 次页面错误?为什么?我没有读取任何内存页。
_start:
标签让我认为正常的初始化已经执行。此外,问题中没有说明如何禁止 libc 链接:如果你没有明确尝试这样做,生成一个没有这些东西的可执行文件就非常困难。无论我的猜测是否正确,我都不会删除我的评论:这个链接真的很好,对其他读者也可能很有趣。 - cmaster - reinstate monicald foo.o -o foo
会生成一个静态可执行文件。而且,动态链接的原因不是依赖于内核头文件(用户空间ABI是稳定的,你不需要不同的glibc来匹配你的内核)。原因是为了避免将libc嵌入到每个二进制文件中,并且glibc的错误修复不需要重新编译所有东西。此外还有通常的内存优势。使用gcc,要想用自定义的_start
创建静态可执行文件,请使用gcc foo.o -nostdlib -static
。或者,要将libc与您自己的_start
动态链接,请使用gcc foo.o -nostartfiles
(glibc init函数仍然会运行)。 - Peter Cordeslibc.so.6
在一个_global_ctors
数组或类似的东西中有必要的初始化函数地址,动态链接器在跳转到_start
之前调用它们。如果gcc告诉链接器这样做,链接器只会“插入代码来调用main
”(即C运行时启动文件)。如果你运行gcc -v foo.c
,你会看到编译器、汇编器和链接器的命令行(ld
通过collect2
包装器调用,但你可以看到crt0.o
由gcc
显式传递)。 - Peter Cordes