rdtsc(Read Time Stamp Counter)のクラス化で、rdtscの取得方法を説明した。今回は何度も同じルーチンのパフォーマンスを測定する上で平均的なrdtscを測定してみる。
測定
下記のようなコードを作ってみた。今回、特にこのクラスを使う上でのオーバーヘッドはあまり考えてない(というより考えなくてよいぐらい重い処理を測定する場合に使うことを前提に)。また、includeにATL/WTL系のヘッダーファイルを使っているが、これはCAtlArrayとCStringを使うためである。vectorやstringが好きな人はそちらを使ってもらえればよいかと。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include <atlbase.h> #include <atlapp.h> #include <atlcoll.h> #include <atlmisc.h> class MeanRdTsc { private: static CAtlArray < long long > rdTsc_; static int size_; long long oldRdTsc_; unsigned int count_; inline static long long getRdTsc() { #ifdef _M_IX86 _asm { rdtsc } #endif #ifdef _M_X64 return __rdtsc(); #endif }; public: MeanRdTsc() : oldRdTsc_( getRdTsc() ), count_( 0 ) { }; ~MeanRdTsc() { size_++; }; void update() { const long long newRdTsc = getRdTsc(); if( rdTsc_.GetCount() <= count_ ) { if( !rdTsc_.SetCount( count_ + 1 ) ) { return; } } rdTsc_[ count_ ] += newRdTsc - oldRdTsc_; count_++; oldRdTsc_ = getRdTsc(); }; }; __declspec( selectany ) int MeanRdTsc::size_ = 0; __declspec( selectany ) CAtlArray < long long > MeanRdTsc::rdTsc_; |
という感じである。staticを使っているが、これは下のような使い方をするためである。
1 2 3 4 5 6 7 8 9 10 11 12 |
for( int i = 0; i < 100; i++ ) { MeanRdTsc mrt; printf( "test1" ); mrt.update(); printf( "test2" ); mrt.update(); } |
ちなみに、ヘッダーファイルしか用意していないので、staticメンバの初期化に__declspec(selectany)を使っている。
平均の出力
出力としてはこんなメソッドを用意すればよいのではないかと。平均ということで回数で割っている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
static void trace() { if( size_ <= 0 ) { return; } CString temp; CString trace; for( int i = 0; i < ( int )rdTsc_.GetCount(); i++ ) { temp.Format( _T( "%I64d" ), rdTsc_[ i ] / size_ ); if( !trace.IsEmpty() ) { trace += _T( "," ); } trace += temp; } ATLTRACE2( CT2A( trace + _T( "\n" ) ) ); }; /// プリント static void print() { if( size_ <= 0 ) { return; } CString temp; CString trace; for( int i = 0; i < ( int )rdTsc_.GetCount(); i++ ) { temp.Format( _T( "%I64d" ), rdTsc_[ i ] / size_ ); if( !trace.IsEmpty() ) { trace += _T( "," ); } trace += temp; } printf( CT2A( trace + _T( "\n" ) ) ); }; |
毎回、もしくは最後にこれらを呼んであげればよいかと。printfやUnicodeバージョンのwprintfがあるが、MSDNで調べたところでは(それを引用して)、
現在 printf では、UNICODE ストリームへの出力はサポートされていません。
とのことで、結局CT2AなどでANSI文字列に変換し普通にprintfを使うのがよいであろう。