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

时间:2015-06-08 12:39:38

标签: linux performance benchmarking performancecounter perf

众所周知,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();
}

5 个答案:

答案 0 :(得分:2)

最后,我找到了一个库来获取一段代码的计数器。

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

答案 1 :(得分:1)

我确实做了一些调查来解决我项目中的同样问题。我找到了另一个名为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.

答案 2 :(得分:0)

听起来你在寻找分析。

如你所说,你在Linux下,所以看看gprof工具链。您只需要使用一些编译器选项编译您的prog并启动您的程序。 gprof之后检查生成的分析数据并提供包含每个代码块的信息的结果。

首先:使用其他选项编译你的编程:

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

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

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

第三:运行你的前卫

./<target>

之后,获取统计数据:

gprof <target>

答案 3 :(得分:0)

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

Examples

答案 4 :(得分:0)

我面临与你相同的情况,我对此做了一些研究。这是我学到的。首先,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