如何在c编程中使用rdtsc来估计for循环的开销
首先要做的是disassembly
,在Linux上你可以从objdump
获得帮助。 objdump
将帮助您查看代码是如何生成的。如果你对这些传递的参数没有做任何处理,那么这只是从堆栈中获取参数并将它们保存在寄存器中的区别。由于这些操作将花费很少的CPU周期,因此,很难提供时序信息,但在CPU周期方面更容易。
你的问题不是很好摆出。但是,执行rdtsc
指令最可靠的方法是使用内联汇编来调用它,这是所有C编译器完全支持的。由C标准规定的任何定时功能将随实施而变化。英特尔有一个非常好的白皮书,介绍实现rdtsc
东西here的最佳方式。主要问题是无序执行,这可能超出了您的问题范围。
我发现的最好的实现是在this repo,我已经适应了我自己的使用。这组基本的宏,假设你有一个兼容的处理器,将会给你〜每个调用开销的32个时钟周期(你需要为你自己的处理器做测试):
#include <cpuid.h>
#include <stdint.h>
/*** Low level interface ***/
/* there may be some unnecessary clobbering here*/
#define _setClockStart(HIs,LOs) { \
asm volatile ("CPUID \n\t" \
"RDTSC \n\t" \
"mov %%edx, %0 \n\t" \
"mov %%eax, %1 \n\t": \
"=r" (HIs), "=r" (LOs):: \
"%rax", "%rbx", "%rcx", "%rdx"); \
}
#define _setClockEnd(HIe,LOe) { \
asm volatile ("RDTSCP \n\t" \
"mov %%edx, %0 \n\t" \
"mov %%eax, %1 \n \t" \
"CPUID \n \t": "=r" (HIe), "=r" (LOe):: \
"%rax", "%rbx", "%rcx", "%rdx"); \
}
#define _setClockBit(HIs,LOs,s,HIe,LOe,e) { \
s=LOs | ((uint64_t)HIs << 32); \
e=LOe | ((uint64_t)HIe << 32); \
}
/*** High level interface ***/
typedef struct {
volatile uint32_t hiStart;
volatile uint32_t loStart;
volatile uint32_t hiEnd;
volatile uint32_t loEnd;
volatile uint64_t tStart;
volatile uint64_t tEnd;
/*tend-tstart*/
uint64_t tDur;
} timer_st;
#define startTimer(ts) \
{ \
_setClockStart(ts.hiStart,ts.loStart); \
}
#define endTimer(ts) \
{ \
_setClockEnd(ts.hiEnd,ts.loEnd); \
_setClockBit(ts.hiStart,ts.loStart,ts.tStart, \
ts.hiEnd,ts.loEnd,ts.tEnd); \
ts.tDur=ts.tEnd-ts.tStart; \
}
#define lapTimer(ts) \
{ \
ts.hiStart=ts.hiEnd; \
ts.loStart=ts.loEnd; \
}
然后像这样的东西
#include <stdio.h>
#include <math.h>
#include "macros.h" /* Macros for calling rdtsc above */
#define SAMPLE_SIZE 100000
int main()
{
timer_st ts;
register double mean=0;
register double variance=0;
int i;
/* "Warmup" */
for(i=1;i<SAMPLE_SIZE;i++)
{
startTimer(ts);
endTimer(ts);
}
/* Data collection */
for(i=1;i<SAMPLE_SIZE;i++)
{
startTimer(ts);
endTimer(ts);
mean+=ts.tDur;
}
mean/=SAMPLE_SIZE;
fprintf(stdout,"SampleSize: %d\nMeanOverhead: %f\n", SAMPLE_SIZE,mean);
return 0;
}
把它在我的Broadwell微架构的芯片,我得到这个输出
SampleSize: 100000
MeanOverhead: 28.946490
29时钟分辨率的时钟分辨率是相当不错的。人们通常使用的任何库函数(如gettimeofday
)都不具备时钟级精度和200-300的开销。
我不确定你的意思是“硬件开销”与“软件开销”,但对于上面的实现,没有函数调用来执行rdtsc
调用之间的时间或中间代码。所以我想软件开销是零。
'rdtsc'本质上是芯片专用的,并且仅限于(某些)英特尔CPU。编译器通常支持'asm',但是使用的符号有所不同 - 然而,它不是由C标准规定的(例如,'asm'不是标准C-ISO/IEC 9899:2011中的关键字)。标准的C(或POSIX)定时功能在各种平台和CPU类型中更加可靠,并且性能可靠 - 尽管(比编译器直接访问“rdtsc”指令更慢)。 –
'gettimeofday'在任何POSIX系统上都能很好地工作。 'rdtsc'指令只存在于x86架构上,并且在所有这些指令上都不起作用,所以我建议避免它。 –
@BenVoigt'clock_gettime'现在正在取代'gettimeofday'。我不确定何时,但手册页说它将来会被删除。 – tijko
@tijko:有'gettimeofday()'的平台没有'clock_gettime()' - Mac OS X(10.11.4)就是这样。目前,'gettimeofday()'比'clock_gettime()'更具可移植性。 –