众所周知, perf
是获取程序CPU性能计数器的工具,比如cache-miss
, cache-reference
, instruction executed
等。
问题:如何在c
或c++
中的一个程序中获取这些性能计数器的一小段代码(如函数)。
例如,我的程序首先做一些初始化,然后做这个工作,然后敲定,我只是想得到性能计数器的工作,如函数do_something_1
。
int main(int argc, char ** argv) { do_initialize(); for (int i = 0;i < 100 ;i ++) { /* begin profile code */ do_something_1(); /* end profile code */ do_something_2(); } do_finalize(); }
最后,我找到一个库来获取这些计数器的一段代码。
PAPI
例如,如果您想测量L3数据缓存读取某段代码。
#include "papi.h" #include <iostream> #include <glog/logging.h> #define ASIZE 2684354560 #define event_count (1) // the number of event you want to trace int main(int argc, char ** argv) { int events[event_count] = {PAPI_L3_DCR}; // L3 Data Cache Read int ret; long long int values[event_count]; // result int* array = new int [ASIZE ]; /* start counters */ ret = PAPI_start_counters(events, event_count); CHECK_EQ(ret, PAPI_OK); size_t tot_cnt = 1; for(size_t cnt = 0; cnt < tot_cnt; cnt ++) { for(size_t i = 0;i < ASIZE ;i ++) { array[i] = i; } } /* read counters */ ret = PAPI_read_counters(values, event_count); CHECK_EQ(ret, PAPI_OK); for(size_t i = 0;i < event_count ;i ++) { LOG(INFO) << " " << values[i]; } return 0; }
Makefile:
CXX?=g++ INC?=-I<path to where papi is installed>/include/ LIB?=-L<path to where papi is installed>/lib/ -lpapi -lglog main : main.cpp ${CXX} -O3 ${INC} -o $@ $< ${LIB} all : main .PHONY: clean : rm -f main
我做了一些调查来解决我的项目中的同样的问题。 我确实找到了另一个名为SkyPat的框架( https://skypat.skymizer.com ),它可以得到像PAPI这样的代码的PMU计数器。
我已经尝试了PAPI和SkyPat来获得一个函数的PMU计数器。 我认为他们之间的区别是SkyPat结合了单元测试和perf_evnet。 它提到了Google Test的概念,并提供了访问PMU的界面,因此很容易与Google Test整合。
例如,如果你想测量一个函数的缓存引用和缓存。
#include <unistd.h> #include "pat/pat.h" #include "test.h" PAT_F(MyCase, my_test) { int result = 0; COUNT(pat::CONTEXT_SWITCHES) { test(10); } COUNT(pat::CPU_CLOCK) { test(10); } COUNT(pat::TASK_CLOCK) { test(10); } COUNT(pat::CACHE_REFERENCES) { test(10); } COUNT(pat::CACHE_MISSES) { test(10); } } int main(int argc, char* argv[]) { pat::Test::Initialize(&argc, argv); pat::Test::RunAll(); }
和SkyPat的结果日志。
[ pat ] Running 1 tests from 1 cases. [----------] 1 test from MyCase. [ RUN ] MyCase.my_test [ TIME (ns)] 2537 1000 843 1855 1293 [EVENT TYPE] [CTX SWITCH] [CPU CLOCK] [TASK CLOCK] [CACHE REF] [CACHE MISS] [RESULT NUM] 0 982 818 2 0 [==========] 1 test from 1 cases ran. [ PASSED ] 1 test.
听起来你在寻找分析。
正如你所说你在Linux下,所以看看gprof工具链。 简单地说,你必须用一些编译器选项来编译你的编程并启动你的程序。 gprof之后检查生成的分析数据并提供包含每个代码块的信息的结果。
首先:用另外的选项编译你的prog:
g++ <source> -c -g -pg ...
第二:链接,你也需要这些选项!
g++ <object1> <object2> ... <objectn> -g -pg -o <target>
第三:运行你的前卫
./<target>
之后,得到统计数据:
gprof <target>
你可以使用operf(oprofile)。
简而言之:
# Build you program with debugging information # Start up the profiler operf /path/to/mybinary # generate a profile summary opreport --symbols # produce some annotated source opannotate --source --output-dir=/path/to/annotated-source
示例注释输出:
$ opannotate --source --output-dir=/home/moz/src/annotated `which oprofiled` $ vi /home/moz/src/annotated/home/moz/src/oprofile/daemon/opd_image.c # the annotated source output ... :static uint64_t pop_buffer_value(struct transient * trans) 254 2.4909 :{ /* pop_buffer_value total: 2105 20.6433 */ : uint64_t val; : 160 1.5691 : if (!trans->remaining) { : fprintf(stderr, "BUG: popping empty buffer !\n"); : exit(EXIT_FAILURE); : } : : val = get_buffer_value(trans->buffer, 0); 123 1.2062 : trans->remaining--; 65 0.6374 : trans->buffer += kernel_pointer_size; : return val; 230 2.2556 :}
例子
我面临着和你一样的情况,我对此做了一些研究。 这是我学到的东西。 首先,perf是作为内核的一部分包含的,你可以检查它的头文件
/usr/src/kernels/$VERSION/include/linux/perf_regs.h /usr/src/kernels/$VERSION/include/linux/perf_event.h /usr/src/kernels/$VERSION/include/uapi/linux/perf_event.h
我认为核心文件是perf_event.h你也可以检查它的github网站,它有一些关于如何使用它的说明。 但目前还不清楚,现在我还有很多混乱。
另外,我发现一个非常有用的库叫做pfmlib,它是编程perf事件的辅助库。 它有示例和perf_examples指示如何在代码级别执行此操作。 我仍在努力。 希望这对你有所帮助。 如果你有问题,我们可以互相学习。
pfmlib的网站是http://perfmon2.sourceforge.net 。