在 C 中用 `perf_event` 计算 CPU 周期产生的值不同于 `perf`
Counting CPU cycles with `perf_event` in C yields different value than `perf`
我尝试通过一个简短的 C 代码片段来计算单个进程的 CPU 周期。 MWE 是 cpucycles.c。
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 利用率的进程,并通过 perf
计算它的 CPU 周期作为还有我的 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
显然,对于我的 3 GHz CPU,只有来自“perf”的“3072358388”CPU 周期是正确的。为什么我的“cpucycles”会返回如此小的嘲笑值?
设置 pe.exclude_kernel = 1;
时,您在分析中排除了内核。
我刚刚验证了通过将该标志设置为 0,我得到了大数字,而将它设置为 1,我得到了小数字。
cat /dev/urandom > /dev/null
几乎所有 cpu 时间都在内核中。 userland 位将是对缓冲区的读取和从该缓冲区的写入,而在这种情况下所有繁重的工作都由内核完成。
我尝试通过一个简短的 C 代码片段来计算单个进程的 CPU 周期。 MWE 是 cpucycles.c。
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 利用率的进程,并通过 perf
计算它的 CPU 周期作为还有我的 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
显然,对于我的 3 GHz CPU,只有来自“perf”的“3072358388”CPU 周期是正确的。为什么我的“cpucycles”会返回如此小的嘲笑值?
设置 pe.exclude_kernel = 1;
时,您在分析中排除了内核。
我刚刚验证了通过将该标志设置为 0,我得到了大数字,而将它设置为 1,我得到了小数字。
cat /dev/urandom > /dev/null
几乎所有 cpu 时间都在内核中。 userland 位将是对缓冲区的读取和从该缓冲区的写入,而在这种情况下所有繁重的工作都由内核完成。