2016-05-01 51 views
2

我想在0到7的範圍內通過增加參數來計算一個函數的開銷。如何估算硬件開銷和軟件開銷。如何在c編程中使用rdtsc來估計for循環的開銷

+0

'gettimeofday'在任何POSIX系統上都能很好地工作。 'rdtsc'指令只存在於x86架構上,並且在所有這些指令上都不起作用,所以我建議避免它。 –

+0

@BenVoigt'clock_gettime'現在正在取代'gettimeofday'。我不確定何時,但手冊頁說它將來會被刪除。 – tijko

+1

@tijko:有'gettimeofday()'的平臺沒有'clock_gettime()' - Mac OS X(10.11.4)就是這樣。目前,'gettimeofday()'比'clock_gettime()'更具可移植性。 –

回答

1

首先要做的是disassembly,在Linux上你可以從objdump獲得幫助。 objdump將幫助您查看代碼是如何生成的。如果你對這些傳遞的參數沒有做任何處理,那麼這只是從堆棧中獲取參數並將它們保存在寄存器中的區別。由於這些操作將花費很少的CPU週期,因此,很難提供時序信息,但在CPU週期方面更容易。

2

你的問題不是很好擺出。但是,執行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調用之間的時間或中間代碼。所以我想軟件開銷是零。

+0

'rdtsc'本質上是芯片專用的,並且僅限於(某些)英特爾CPU。編譯器通常支持'asm',但是使用的符號有所不同 - 然而,它不是由C標準規定的(例如,'asm'不是標準C-ISO/IEC 9899:2011中的關鍵字)。標準的C(或POSIX)定時功能在各種平臺和CPU類型中更加可靠,並且性能可靠 - 儘管(比編譯器直接訪問「rdtsc」指令更慢)。 –