如何获取CPU性能计数器的一段代码



众所周知,perf是获取程序CPU性能计数器的工具,如cache-misscache-referenceinstruction executed等。

问题:如何在cc++的一个程序中仅为一段代码(如函数)获得这些性能计数器。例如,我的程序首先做一些初始化,然后做工作,然后完成,我只是想获得工作的性能计数器,如函数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();
}

最后,我找到了一个库来获取这些计数器。

爸爸

例如,如果您想测量某些代码段的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

可以使用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 :}
示例

我确实做了一些调查来解决我的项目中同样的问题。我确实找到了另一个名为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下,所以看看gproftoolchain。简单地说,您必须使用一些编译器选项编译程序并启动程序。Gprof之后检查生成的分析数据,并提供包含每个代码块信息的结果。

首先:用附加选项编译程序:

g++ <source> -c -g -pg
...

第二:链接,你也需要这些选项!

g++ <object1> <object2> ... <objectn> -g -pg -o <target>

第三:运行你的程序

./<target>

之后,获取统计信息:

gprof <target>

我面临着和你一样的情况,我对此做了一些研究。以下是我所学到的。首先,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_examples来指导如何在代码级完成此操作。我还在努力。希望这对你有所帮助。如果你有问题,我们可以互相学习。

pfmlib的网址是http://perfmon2.sourceforge.net

最新更新