2016-05-01 6 views
2

0から7の範囲でパラメータを増やして関数のパラメータのオーバーヘッドを計算したいとします。ハードウェアオーバーヘッドとソフトウェアオーバーヘッドを見積もる方法。cプログラミングでrdtscを使ってforループのオーバーヘッドを見積もる方法

+0

'gettimeofday'はどのPOSIXシステムでもうまく動作します。 'rdtsc'命令はx86アーキテクチャ上にのみ存在し、すべてで同じように動作しないので、回避することをお勧めします。 –

+0

@BenVoigt 'clock_gettime'が' gettimeofday'を置き換えています。私は、いつmanページがそれが将来削除されると言いますか分からない。 – tijko

+1

@tijko:まだ 'clock_gettime()'がない 'gettimeofday()'のプラットフォームがあります - Mac OS X(10.11.4)はそのようなものです。現時点では、 'gettimeofday()'は 'clock_gettime()'より移植性があります。 –

答えて

1

最初に行うことはdisassemblyです.Linuxではobjdumpからお手伝いできます。 objdumpは、コードの生成方法を確認するのに役立ちます。あなたが渡されたパラメータで何もしていないならば、それはスタックからのパラメータを取ってそれらをレジスタ上に保つことの違いにすぎません。これらのmovの操作にはCPUのサイクル数がほとんどかからないため、タイミング情報を得るのは困難ですが、CPUサイクルは簡単です。

2

あなたの質問は本当によくポーズされていません。しかし、rdtsc命令を実行する最も信頼性の高い方法は、すべてのCコンパイラによって完全にサポートされているインラインアセンブリで呼び出すことです。 C標準で規定されているタイミング関数は実装によって異なります。インテルは、rdtscのものhereを実装する最良の方法について、本当に良い白書を持っています。主な関心事は、あなたの質問の範囲外である可能性のある順序外の実行です。

私が見つけた最良の実装はthis repoで、これは私が自分で使用するために調整したものです。

#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;              \ 
} 
:マクロのこの基本セットは、あなたが互換性のあるプロセッサを持っていると仮定すると、あなたを与える〜32クロックは、各コール(あなたがあなた自身のプロセッサのためのテストを行う必要があります)上のオーバーヘッドのダニ

その後、私のBroadwellマイクロアーキテクチャのチップでは、この

#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; 
} 

のようなもので、それを呼び出す私はこの出力を得た

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'命令に直接アクセスするよりも遅くても、信頼性高く動作します。 –

関連する問題