【发布时间】:2018-07-26 08:01:18
【问题描述】:
我正在尝试了解如何衡量性能并决定编写一个非常简单的程序:
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 个页面错误?为什么?我没有读取任何内存页。
【问题讨论】:
-
47654条指令你怎么不担心? :) 不确定 perf 到底计算了什么,但大概它正在执行的其他任何东西(内核代码?)也是造成停顿的原因。
-
如果你复制/粘贴你的真实代码(和构建指令),它一开始就不可能存在。总是这样做而不是重新输入代码。如果您正在编写代码并且尚未在任何地方对其进行过测试,请仅在 SO 中键入代码。
-
@PeterCordes 正是缺少的
_start:标签让我认为执行了正常的初始化。此外,在如何抑制 libc 链接的问题中也缺少描述:如果您没有明确尝试这样做,那么如果没有这些东西,就很难生成可执行文件。无论如何,不管我的猜测是否正确,我都不会删除我的评论:链接真的很好,其他读者也可能很感兴趣。 -
@cmaster:
ld foo.o -o foo在 Linux 上生成静态可执行文件。不,动态链接的原因不是对内核头文件的依赖(用户空间 ABI 是稳定的,你不需要不同的 glibc 来匹配你的内核)。原因是 libc 没有嵌入到每个二进制文件中,并且 glibc 错误修复不需要重新编译 everything。加上通常的内存优势。使用 gcc,要使用自定义_start创建静态可执行文件,请使用gcc foo.o -nostdlib -static。或者将 libc 与您自己的_start、gcc foo.o -nostartfiles动态链接(glibc init 函数仍然运行)。 -
@cmaster:glibc init 函数使用与在动态加载的 C++ 库中运行构造函数相同的机制运行:
libc.so.6在_global_ctors数组或类似的东西中具有必要的 init 函数的地址,动态链接器在跳转到_start之前调用它。如果 gcc 告诉它,链接器只会“插入代码以...调用main”(即 CRT(C 运行时)启动文件)。如果您运行gcc -v foo.c,您将看到编译器、汇编器和链接器命令行(ld通过collect2包装器调用,但您可以看到crt0.o由gcc显式传递)
标签: linux performance assembly x86-64 perf