还有哪些其他程序与gprof做相同的事情?
还有哪些其他程序与gprof做相同的事情?
Answers:
Valgrind有一个指令计数探查器,带有一个非常好的可视化器,称为KCacheGrind。正如Mike Dunlavey所建议的那样,Valgrind会计算过程中存在于堆栈中的指令所占的比例,尽管我很遗憾地说,在存在相互递归的情况下,该过程似乎很混乱。但是可视化器非常好,提前了几年gprof
。
gprof (阅读本文)存在是出于历史原因。如果您认为它可以帮助您发现性能问题,则永远不会这样宣传。这篇文章说的是:
该配置文件可用于比较和评估各种实现的成本。
它没有说它可以用来识别要评估的各种实现,尽管它暗示它可以在特殊情况下:
尤其是在发现程序的一小部分控制其执行时间的情况下。
那不是那么本地化的问题呢?那些没关系吗?不要对从来没有要求过的gprof寄予期望。它仅是一种测量工具,并且仅用于受CPU约束的操作。
试试这个吧。
这是一个44倍加速的示例。
这是730倍的加速比。
这是一个8分钟的视频演示。
这是统计信息的解释。
这是批评的答案。
关于程序有一个简单的观察。在给定的执行中,每条指令负责整个时间的一部分(尤其是call
指令),从某种意义上说,如果不存在,则不会花费时间。在此期间,指令位于堆栈**中。了解这一点后,您可以看到-
gprof体现了有关性能的某些神话,例如:
程序计数器采样很有用。
仅当您有不必要的热点瓶颈(例如大量标量值的冒泡类型)时才有用。例如,一旦您使用string-compare将其更改为某种类型,它仍然是一个瓶颈,但是程序计数器采样不会看到它,因为现在热点在string-compare中。另一方面,如果要对扩展程序计数器(调用堆栈)进行采样,则会清晰显示调用字符串比较的点(排序循环)。实际上,gprof旨在弥补仅PC采样的局限性。
计时功能比捕获费时的代码行更重要。
产生这种说法的原因是gprof无法捕获堆栈样本,因此它对函数进行计时,计算其调用次数并尝试捕获调用图。但是,一旦确定了一项昂贵的功能,您仍然需要在其内部查找造成该时间的线路。如果有堆栈样本,则无需查看,这些行将在样本上。(一个典型的函数可能有100-1000条指令。一个函数调用是1条指令,因此定位昂贵调用的函数要精确2-3个数量级。)
调用图很重要。
您需要了解的程序不是花时间在哪里,而是为什么。当花时间在一个函数上时,堆栈中的每一行代码都会在其原因推理链中给出一个链接。如果您只能看到堆栈的一部分,那么只能看到部分原因,因此无法确定是否确实需要该时间。通话图会告诉您什么?每条弧线都告诉您某些函数A在一段时间内正在调用某些函数B。即使A只有一行这样的代码调用B,该行也仅给出了原因的一小部分。如果您足够幸运,也许那条线没有充分的理由。通常,您需要查看多条并发的行以找到不良原因(如果存在)。如果A呼叫B的地点多于一个,那么它告诉您的甚至更少。
递归是一个棘手的令人困惑的问题。
这仅仅是因为gprof和其他分析器认为需要生成调用图,然后将时间归因于节点。如果有堆栈样本,则出现在样本上的每一行代码的时间成本是一个非常简单的数字-所占样本的比例。如果存在递归,则给定的行可以在一个样本上出现多次。
不管。假设每N ms采样一次,并且该行出现在它们的F%上(无论是否出现)。如果可以使该行花费时间(例如通过删除它或在其周围分支),则这些样本将消失,时间将减少F%。
时间测量的准确性(因此需要大量样本)非常重要。
想一想。如果一行代码位于五个样本中的三个样本上,那么如果您可以像灯泡一样将其发射出去,则可以节省大约60%的时间。现在,您知道,如果您另取5个样本,则可能只看过2次,或多达4次。因此60%的测量更像是从40%到80%的一般范围。如果只有40%,您是否认为这个问题不值得解决?那么,当您真正想要的是发现问题时,时间准确性的重点是什么?500或5000个样本本可以以更高的精度测量问题,但找不到更准确的问题。
计数语句或函数调用很有用。
假设您知道某个函数已被调用1000次。您能从中得知花费多少时间吗?您还需要知道平均需要运行多长时间,然后将其乘以计数,再除以总时间。平均调用时间可能在纳秒到几秒之间变化,因此仅凭计数并不能说明什么。如果有堆栈样本,那么例程或任何语句的成本只是它所使用的样本的一小部分。如果可以使例程或语句不花时间,那么原则上可以节省整个时间,这是与性能最直接的关系。
该封端当样品不必采取
这种神话的原因是双重的:1)PC采样是无意义的,当程序处于等待状态,以及2)与定时准确度的当务之急。但是,对于(1),程序很可能在等待它所要求的内容,例如文件I / O,您需要知道这些内容,以及哪些堆栈样本可以显示。(显然,您希望在等待用户输入时排除样本。)对于(2),如果程序仅由于与其他进程的竞争而在等待,那么大概是在运行时以相当随机的方式发生。因此,尽管程序可能花费更长的时间,但这不会对重要的统计数据产生太大的影响,而是语句在堆栈上的时间百分比。
“自我时间”很重要
自我时间仅在您在功能级别(而不是线路级别)进行测量时才有意义,并且您认为在识别功能时间是否用于纯本地计算而非调用例程中时,您需要帮助。如果在行级别进行汇总,则行表示自身时间(如果它位于堆栈的末尾),否则代表包含时间。不管哪种方式,它的成本都是它在堆栈样本上的百分比,因此无论哪种情况都可以找到。
必须以高频率进行采样
这是基于这样的想法,即性能问题可能正在迅速解决,并且必须频繁采样才能达到目标。但是,如果问题耗费成本,例如,在10秒(或任何其他时间)的总运行时间中占20%,则在此总时间内的每个样本都将有20%的机会实现目标,无论是否发生问题
无论是像这样的单个片段
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20个样本,4个匹配),
或者像这样的许多小片段
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20个样本,3个匹配),
无论采用多少个样本,匹配的次数平均为5分之一。几个 (平均值= 20 * 0.2 =4。标准偏差= +/- sqrt(20 * 0.2 * 0.8)= 1.8。)
你正在努力寻找的瓶颈
好像只有一个有。请考虑以下执行时间表:vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
它由表示有用的实际工作组成.
。在分别vWxYz
花费1 / 2、1 / 4、1 / 8、1 / 16、1 / 32的时间存在性能问题。采样v
容易找到。它已被删除,离开了。
xWzWxWYWxW.WxWYW
现在程序的运行W
时间是原来的一半,而现在的时间是原来的一半,而且很容易找到。它被删除,离开
xzxYx.xY
此过程继续进行,每次删除百分比最大的性能问题,直到找不到要删除的内容。现在,唯一执行的是.
,它执行的时间是原始程序使用时间的1/32。这是放大效果,由于分母减少了,因此消除任何问题都会使其余的百分比增加。
另一个关键点是,必须找到每个问题 -都不遗漏5个。未找到并修复的任何问题都会严重降低最终的加速比。仅找到一些但不是全部,还不够好。
新增:我只想指出gprof受欢迎的原因之一-有人在教它,大概是因为它是免费的,容易教的,而且已经存在了很长时间。通过Google的快速搜索,可以找到一些教过它(或看起来如此)的学术机构:
伯克利bu克莱姆森科罗拉多杜克厄尔勒姆fsu印第安纳州密西西比州ncsa.illinois ncsu nyu ou princeton psu斯坦福ucsd umd umich utah utxas utk wustl
**除要求完成工作的其他方式外,不要留下任何告诉原因的痕迹,例如通过消息发布。
由于我在这里看不到任何关于perf
在Linux上对内核和用户应用程序进行性能分析的相对较新的工具,因此我决定添加此信息。
perf
如果您的Linux内核大于2.6.32或oprofile
更旧,则可以使用。这两个程序都不需要您来检测您的程序(就像require一样gprof
)。但是,为了正确获取调用图,perf
您需要使用构建程序-fno-omit-frame-pointer
。例如:g++ -fno-omit-frame-pointer -O2 main.cpp
。
您可以通过以下方式对应用程序进行“实时”分析perf top
:
sudo perf top -p `pidof a.out` -K
或者,您可以记录正在运行的应用程序的性能数据,然后对其进行分析:
1)记录性能数据:
perf record -p `pidof a.out`
或录制10秒:
perf record -p `pidof a.out` sleep 10
或使用通话记录()进行记录
perf record -g -p `pidof a.out`
2)分析记录的数据
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
或者,您可以记录应用程序的性能数据,然后通过以这种方式启动应用程序并等待其退出来对其进行分析:
perf record ./a.out
这是分析测试程序的示例
测试程序位于文件main.cpp中(我将main.cpp放在消息的底部):
我以这种方式编译它:
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
我使用libmalloc_minimial.so
它是因为-fno-omit-frame-pointer
libc malloc似乎是在没有此选项的情况下编译的。然后我运行测试程序
./my_test 100000000
然后,我记录正在运行的进程的性能数据:
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
然后,我分析每个模块的负载:
性能报告--stdio -g无--sort comm,dso -i ./my_test.perf.data
# Overhead Command Shared Object
# ........ ....... ............................
#
70.06% my_test my_test
28.33% my_test libtcmalloc_minimal.so.0.1.0
1.61% my_test [kernel.kallsyms]
然后分析每个函数的负载:
性能报告--stdio -g none -i ./my_test.perf.data | C ++过滤
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
29.14% my_test my_test [.] f1(long)
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
9.44% my_test my_test [.] process_request(long)
1.01% my_test my_test [.] operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
0.13% my_test [kernel.kallsyms] [k] native_write_msr_safe
and so on ...
然后分析呼叫链:
性能报告--stdio -g图-i ./my_test.perf.data | C ++过滤
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
|
--- f2(long)
|
--29.01%-- process_request(long)
main
__libc_start_main
29.14% my_test my_test [.] f1(long)
|
--- f1(long)
|
|--15.05%-- process_request(long)
| main
| __libc_start_main
|
--13.79%-- f2(long)
process_request(long)
main
__libc_start_main
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
|
--- operator new(unsigned long)
|
|--11.44%-- f1(long)
| |
| |--5.75%-- process_request(long)
| | main
| | __libc_start_main
| |
| --5.69%-- f2(long)
| process_request(long)
| main
| __libc_start_main
|
--3.01%-- process_request(long)
main
__libc_start_main
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
|
--- operator delete(void*)
|
|--9.13%-- f1(long)
| |
| |--4.63%-- f2(long)
| | process_request(long)
| | main
| | __libc_start_main
| |
| --4.51%-- process_request(long)
| main
| __libc_start_main
|
|--3.05%-- process_request(long)
| main
| __libc_start_main
|
--0.80%-- f2(long)
process_request(long)
main
__libc_start_main
9.44% my_test my_test [.] process_request(long)
|
--- process_request(long)
|
--9.39%-- main
__libc_start_main
1.01% my_test my_test [.] operator delete(void*)@plt
|
--- operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
|
--- operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
and so on ...
这样,您就知道了程序在哪里花费时间。
这是用于测试的main.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t f1(time_t time_value)
{
for (int j =0; j < 10; ++j) {
++time_value;
if (j%5 == 0) {
double *p = new double;
delete p;
}
}
return time_value;
}
time_t f2(time_t time_value)
{
for (int j =0; j < 40; ++j) {
++time_value;
}
time_value=f1(time_value);
return time_value;
}
time_t process_request(time_t time_value)
{
for (int j =0; j < 10; ++j) {
int *p = new int;
delete p;
for (int m =0; m < 10; ++m) {
++time_value;
}
}
for (int i =0; i < 10; ++i) {
time_value=f1(time_value);
time_value=f2(time_value);
}
return time_value;
}
int main(int argc, char* argv2[])
{
int number_loops = argc > 1 ? atoi(argv2[1]) : 1;
time_t time_value = time(0);
printf("number loops %d\n", number_loops);
printf("time_value: %d\n", time_value );
for (int i =0; i < number_loops; ++i) {
time_value = process_request(time_value);
}
printf("time_value: %ld\n", time_value );
return 0;
}
f1
在打电话delete
。40%(大约)的时间process_request
在打电话delete
。其余的很大一部分都花在了new
。测量值很粗糙,但是热点是精确的。
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace
。1)我认为当您需要分析客户服务器上运行的程序的性能问题时,您的技术没有用。2)我不确定您如何应用此技术来获取程序的信息,该程序具有许多处理不同请求的线程。我的意思是总体情况非常复杂。
the problem is outside your code
,是吗?因为您可能需要一些信息才能支持您的观点。在这种情况下,您有时可能需要分析您的应用程序。您不能仅仅要求您的客户启动gdb并按^ C并获取调用堆栈。这就是我的意思。这是spielwiese.fontein.de/2012/01/22/…的示例。我遇到了这个问题,分析很有帮助。
试试OProfile。这是用于分析代码的更好的工具。我还建议使用Intel VTune。
上面的两个工具可以缩短花在特定代码行上的时间,注释代码,显示汇编以及特定指令需要多少时间。除了时间指标外,您还可以查询特定的计数器,例如缓存命中率等。
与gprof不同,您可以使用两者之一来分析系统上运行的任何进程/二进制文件。
Google性能工具包括一个易于使用的探查器。CPU和堆分析器都可用。
http://lttng.org/如果您想要高性能的跟踪器