我想通过在0到7范围内增加参数来计算函数参数的开销。如何估算硬件开销和软件开销。

最佳答案

您的问题提出的并不十分正确。但是,执行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调用之间的中间代码。因此,我认为软件开销为零。

关于c - 如何在C编程中使用rdtsc估算for循环的开销,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36961657/

10-13 07:21