我熟悉两种方法,但它们都有其局限性。
第一个是使用指令RDTSC
。然而,问题是它不能单独计算我的程序的周期数,因此对并发进程产生的噪声很敏感。
第二个选项是使用clock
库函数。我认为这种方法是可靠的,因为我希望它只计算我的程序的周期数(我打算实现)。但是,事实证明,在我的情况下,它测量经过的时间,然后乘以CLOCKS_PER_SEC
。这不仅不可靠,而且也是错误的,因为CLOCKS_PER_SEC
设置为1,000,000
,这与我的处理器的实际频率不对应。
鉴于所提方法的局限性,是否有更好,更可靠的替代方法来产生一致的结果?
答案 0 :(得分:2)
这里有很多取决于你尝试测量的时间量。
正确使用时,RDTSC可以(几乎)100%可靠。然而,它主要用于测量真正的微观代码片段。如果你想要测量两个序列,比如几十个左右的指令,那么可能还有其他任何东西都可以完成这项工作。
正确使用它有点挑战性。一般来说,为了获得良好的测量结果,您至少需要做以下几点:
另一方面,如果您尝试测量的东西,例如100毫秒,那么RDTSC
毫无意义。这就像用千分尺测量城市之间的距离一样。为此,通常最好确保所讨论的代码(至少)花费大约一秒左右的时间。 clock
并不是特别精确,但是对于这个一般订单的一段时间,它可能只有10毫秒左右才准确,这或多或少是不相关的。
答案 1 :(得分:0)
RDTSC是计算程序执行周期的最准确方法。如果您希望在时间范围内测量执行性能,如果您的线程已被抢占,那么您可能会更好地使用分析器(例如VTune)。
与RDTSC相比,CLOCKS_PER_SECOND / clock()几乎是一种非常糟糕(低性能)的获取时间的方式,而RDTSC几乎没有开销。
如果您对RDTSC有特定问题,我可能会提供帮助。
re:评论
英特尔性能计数器监视器:这主要用于测量处理器之外的指标,例如内存带宽,电源使用情况,PCIe利用率。它也会测量CPU频率,但它通常对处理器绑定的应用程序性能没有用。
RDTSC 可移植性:RDTSC是所有现代Intel CPU都支持的intel CPU指令。在现代CPU上,它基于CPU的非核心频率,并且在CPU核心上有些相似,尽管如果您的应用程序经常被抢占到不同的核心(尤其是不同的套接字),这是不合适的。如果是这种情况,你真的想看一个分析器。
乱序执行:是的,事情无序执行,因此这可能会略微影响性能,但执行指令仍需要时间,而RDTSC是衡量时间的最佳方法。它在同一核心上执行非IO绑定指令的正常使用情况方面表现优异,这实际上就是它的用途。如果你有一个更复杂的用例,你真的应该使用不同的工具,但这并不能否定rdtsc()在分析程序执行时非常有用。
答案 2 :(得分:0)
使用perf_event_open
的Linux config = PERF_COUNT_HW_CPU_CYCLES
系统调用
此系统调用具有以下明确控件:
,因此即使多个进程同时运行,它也可以正确计数周期。
有关更多详细信息,请参见以下答案:How to get the CPU cycle count in x86_64 from C++?
perf_event_open.c
#include <asm/unistd.h>
#include <linux/perf_event.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <inttypes.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;
}
int
main(int argc, char **argv)
{
struct perf_event_attr pe;
long long count;
int fd;
uint64_t n;
if (argc > 1) {
n = strtoll(argv[1], NULL, 0);
} else {
n = 10000;
}
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;
// Don't count hypervisor events.
pe.exclude_hv = 1;
fd = perf_event_open(&pe, 0, -1, -1, 0);
if (fd == -1) {
fprintf(stderr, "Error opening leader %llx\n", pe.config);
exit(EXIT_FAILURE);
}
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
/* Loop n times, should be good enough for -O0. */
__asm__ (
"1:;\n"
"sub $1, %[n];\n"
"jne 1b;\n"
: [n] "+r" (n)
:
:
);
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
read(fd, &count, sizeof(long long));
printf("%lld\n", count);
close(fd);
}