在 C 中使用“perf_event”计算 CPU 周期会产生与“perf”不同的值

Chi*_*kus 3 c performancecounter cpu-cycles perf

我尝试通过简短的 C 代码片段来计算单个进程的 CPU 周期。MWE 是cpucycles.c

\n\n

cpucycles.c(主要基于手册页示例

\n\n
#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <linux/perf_event.h>\n#include <asm/unistd.h>\n\nstatic long\nperf_event_open(struct perf_event_attr *hw_event, pid_t pid,\n                int cpu, int group_fd, unsigned long flags)\n{\n    int ret;\n    ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,\n                    group_fd, flags);\n    return ret;\n}\n\nlong long\ncpu_cycles(pid_t pid, unsigned int microseconds)\n{\n    struct perf_event_attr pe;\n    long long count;\n    int fd;\n\n    memset(&pe, 0, sizeof(struct perf_event_attr));\n    pe.type = PERF_TYPE_HARDWARE;\n    pe.size = sizeof(struct perf_event_attr);\n    pe.config = PERF_COUNT_HW_CPU_CYCLES;\n    pe.disabled = 1;\n    pe.exclude_kernel = 1;\n    pe.exclude_hv = 1;\n\n    fd = perf_event_open(&pe, pid, -1, -1, 0);\n    if (fd == -1) {\n        return -1;\n    }\n\n    ioctl(fd, PERF_EVENT_IOC_RESET, 0);\n    ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);\n    usleep(microseconds);\n    ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);\n    read(fd, &count, sizeof(long long));\n\n    close(fd);\n    return count;\n}\n\nint main(int argc, char **argv)\n{\n    printf("CPU cycles: %lld\\n", cpu_cycles(atoi(argv[1]), atoi(argv[2])));\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

接下来,我编译它,设置 perf_event 访问权限,启动一个完全使用 CPU 的进程,并通过perf我的cpucycles.

\n\n
$ gcc -o cpucycles cpucycles.c\n$ echo 1 | sudo tee /proc/sys/kernel/perf_event_paranoid\n$ cat /dev/urandom > /dev/null &\n[1] 3214\n$ perf stat -e cycles -p 3214 -x, sleep 1\n3072358388,,cycles,1000577415,100,00,,,,\n$ ./cpucycles 3214 1000000\nCPU cycles: 287953\n
Run Code Online (Sandbox Code Playgroud)\n\n

显然,只有 \xc2\xb4perf\xc2\xb4 中的 \xc2\xb43072358388\xc2\xb4 CPU 周期对于我的 3 GHz CPU 是正确的。为什么我的 \xc2\xb4cpucycles\xc2\xb4 返回如此可笑的小值?

\n

Art*_*Art 6

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

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

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

  • 是的,这就是问题所在。现在它可以正常工作了。谢谢你! (2认同)