用C中`perf_event`计算CPU周期产生的值与`perf`不同#

问题描述:

我尝试通过一个简短的C代码片段来计算单个进程的CPU周期。 MWE是cpucycles.c用C中`perf_event`计算CPU周期产生的值与`perf`不同#

cpucycles.c(主要基于该man page example

#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/ioctl.h> 
#include <linux/perf_event.h> 
#include <asm/unistd.h> 

static long 
perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 
       int cpu, int group_fd, unsigned long flags) 
{ 
    int ret; 
    ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, 
        group_fd, flags); 
    return ret; 
} 

long long 
cpu_cycles(pid_t pid, unsigned int microseconds) 
{ 
    struct perf_event_attr pe; 
    long long count; 
    int fd; 

    memset(&pe, 0, sizeof(struct perf_event_attr)); 
    pe.type = PERF_TYPE_HARDWARE; 
    pe.size = sizeof(struct perf_event_attr); 
    pe.config = PERF_COUNT_HW_CPU_CYCLES; 
    pe.disabled = 1; 
    pe.exclude_kernel = 1; 
    pe.exclude_hv = 1; 

    fd = perf_event_open(&pe, pid, -1, -1, 0); 
    if (fd == -1) { 
     return -1; 
    } 

    ioctl(fd, PERF_EVENT_IOC_RESET, 0); 
    ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); 
    usleep(microseconds); 
    ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); 
    read(fd, &count, sizeof(long long)); 

    close(fd); 
    return count; 
} 

int main(int argc, char **argv) 
{ 
    printf("CPU cycles: %lld\n", cpu_cycles(atoi(argv[1]), atoi(argv[2]))); 
    return 0; 
} 

接下来,我编译它,设置perf_event访问权限,启动全CPU利用率的过程,并通过计算它的CPU周期perf以及我的cpucycles

$ gcc -o cpucycles cpucycles.c 
$ echo 1 | sudo tee /proc/sys/kernel/perf_event_paranoid 
$ cat /dev/urandom > /dev/null & 
[1] 3214 
$ perf stat -e cycles -p 3214 -x, sleep 1 
3072358388,,cycles,1000577415,100,00,,,, 
$ ./cpucycles 3214 1000000 
CPU cycles: 287953 

很明显,'perf'只有'3072358388'CPU周期对我的3 GHz CPU是正确的。为什么我的'cpucycles'返回这样的嘲笑小值?

+0

我认为关键点在于[man](http://man7.org/linux/man-pages/man2/perf_event_open.2.html):_谨慎处理CPU 频率调整过程中发生的情况._ – LPs

+0

不幸的是,没有。即使我将调速器设置为“性能”(无CPU频率调节,始终使用最大频率),结果也是一样的。 – Chickenmarkus

设置pe.exclude_kernel = 1;时,您在分析中排除内核。

我刚刚证实,通过将该标志设置为0,我得到大数字,并将其设置为1我得到小数字。

cat /dev/urandom > /dev/null几乎将所有的CPU时间花在内核中。用户态位将被读取到缓冲区并从该缓冲区写入,而在这种情况下所有繁重的操作都是由内核完成的。

+1

是的,这是问题所在。现在它正常工作。谢谢! – Chickenmarkus