C / C ++:プロセッサー時間の測定方法

画像
KDPV


翻訳者から:
私の友人のほとんどは、C ++のさまざまな種類のベンチマークで時間を測定するために、 chronoまたは特に高度な場合ではctimeを使用しています。 ただし、ベンチマークでは、CPU時間を測定する方がはるかに便利です。 最近、プロセッサ時間のクロスプラットフォーム測定に関する記事に出会い、ここでそれを共有することを決めました。ローカルベンチマークの品質を多少上げる可能性があります。


PS記事が「今日」または「今」と言うとき、それは「記事の公開時」、つまり、私が間違っていなければ、2012年3月を意味します。
PPS公開時点では、オリジナルは利用できませんが、 Yandexキャッシュに保存されています


プロセスが使用するプロセッサ時間を取得できるAPI関数は、Windows、Linux、OSX、BSD、Solaris、および他のUNIX系オペレーティングシステムのオペレーティングシステムによって異なります。 この記事では、プロセスのプロセッサー時間を受け取るクロスプラットフォーム機能を提供し、各OSがサポートする機能について説明します。


プロセッサ時間を取得する方法


プロセスが実行され、CPUサイクルを消費すると、CPU時間は増加します。 I / O、スレッドロック、およびプロセッサを一時停止するその他の操作中、プロセスが再びCPUの使用を開始するまで、プロセッサ時間は増加しません。


POSIXのps 、OSXのActivity Monitor、WindowsのTask Managerなどのさまざまなツールは、プロセスで使用されるプロセッサー時間を表示しますが、プロセス自体から直接追跡すると便利な場合がよくあります。 これは、アルゴリズムのベンチマークや複雑なプログラムの小さな部分で特に役立ちます。 すべてのオペレーティングシステムがプロセッサ時間用のAPIを提供しているという事実にもかかわらず、それぞれに独自の微妙さがあります。


コード


以下のgetCPUTime( )関数は、ほとんどのオペレーティングシステムで動作します(コードをコピーするか、 getCPUTime.cファイルをダウンロードするだけです)。 必要に応じて、 librtにリンクしてPOSIXタイマー(AIX、BSD、Cygwin、HP-UX、Linux、およびSolarisを取得しますが、 OSXは取得しません)。 それ以外の場合は、標準ライブラリで十分です。


次に、すべての機能、微妙さ、およびコードに#ifdefが非常に多く含まれる理由について詳しく説明します。


getCPUTime.c
 /* * Author: David Robert Nadeau * Site: http://NadeauSoftware.com/ * License: Creative Commons Attribution 3.0 Unported License * http://creativecommons.org/licenses/by/3.0/deed.en_US */ #if defined(_WIN32) #include <Windows.h> #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) #include <unistd.h> #include <sys/resource.h> #include <sys/times.h> #include <time.h> #else #error "Unable to define getCPUTime( ) for an unknown OS." #endif /** * Returns the amount of CPU time used by the current process, * in seconds, or -1.0 if an error occurred. */ double getCPUTime( ) { #if defined(_WIN32) /* Windows -------------------------------------------------- */ FILETIME createTime; FILETIME exitTime; FILETIME kernelTime; FILETIME userTime; if ( GetProcessTimes( GetCurrentProcess( ), &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) { SYSTEMTIME userSystemTime; if ( FileTimeToSystemTime( &userTime, &userSystemTime ) != -1 ) return (double)userSystemTime.wHour * 3600.0 + (double)userSystemTime.wMinute * 60.0 + (double)userSystemTime.wSecond + (double)userSystemTime.wMilliseconds / 1000.0; } #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) /* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */ #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) /* Prefer high-res POSIX timers, when available. */ { clockid_t id; struct timespec ts; #if _POSIX_CPUTIME > 0 /* Clock ids vary by OS. Query the id, if possible. */ if ( clock_getcpuclockid( 0, &id ) == -1 ) #endif #if defined(CLOCK_PROCESS_CPUTIME_ID) /* Use known clock id for AIX, Linux, or Solaris. */ id = CLOCK_PROCESS_CPUTIME_ID; #elif defined(CLOCK_VIRTUAL) /* Use known clock id for BSD or HP-UX. */ id = CLOCK_VIRTUAL; #else id = (clockid_t)-1; #endif if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; } #endif #if defined(RUSAGE_SELF) { struct rusage rusage; if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) return (double)rusage.ru_utime.tv_sec + (double)rusage.ru_utime.tv_usec / 1000000.0; } #endif #if defined(_SC_CLK_TCK) { const double ticks = (double)sysconf( _SC_CLK_TCK ); struct tms tms; if ( times( &tms ) != (clock_t)-1 ) return (double)tms.tms_utime / ticks; } #endif #if defined(CLOCKS_PER_SEC) { clock_t cl = clock( ); if ( cl != (clock_t)-1 ) return (double)cl / (double)CLOCKS_PER_SEC; } #endif #endif return -1; /* Failed. */ } 

使用する


アルゴリズムのプロセッサ時間を測定するには、アルゴリズムの開始前後にgetCPUTime( )呼び出し、差を出力します。 単一の関数呼び出しで返される値に意味があると想定しないでください。


 double startTime, endTime; startTime = getCPUTime( ); ... endTime = getCPUTime( ); fprintf( stderr, "CPU time used = %lf\n", (endTime - startTime) ); 

議論


各OSは、プロセッサ時間を取得する1つ以上の方法を提供します。 ただし、一部の方法は他の方法よりも正確です。


OS時計clock_gettimeGetProcessTimes取り返し
エクスはいはいはいはい
BSDはいはいはいはい
HP-UXはいはいはいはい
Linuxはいはいはいはい
OSXはいはいはい
Solarisはいはいはいはい
はい

これらの各方法について、以下で詳しく説明します。


GetProcessTimes()


WindowsおよびCygwin (WindowsのUNIXライクな環境およびコマンドラインインターフェイス)では、 GetProcessTimes()関数はプロセスで使用されるプロセッサ時間をFILETIME構造体に設定FileTimeToSystemTime()関数はFILETIME構造体を使用可能な時間値を含むSYSTEMTIME構造体に変換します。


 typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME; 

GetProcessTimes()の可用性: Cygwin、Windows XP以降。


プロセッサー時間の取得:


 #include <Windows.h> ... FILETIME createTime; FILETIME exitTime; FILETIME kernelTime; FILETIME userTime; if ( GetProcessTimes( GetCurrentProcess( ), &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) { SYSTEMTIME userSystemTime; if ( FileTimeToSystemTime( &userTime, &userSystemTime ) != -1 ) return (double)userSystemTime.wHour * 3600.0 + (double)userSystemTime.wMinute * 60.0 + (double)userSystemTime.wSecond + (double)userSystemTime.wMilliseconds / 1000.0; } 

clock_gettme()


ほとんどのPOSIX互換オペレーティングシステムでは、 clock_gettime( )AIXBSDHP-UXLinux、およびSolarisのマニュアルを参照)が最も正確なプロセッサー時間値を提供します。 関数の最初の引数は「clock id」を選択し、2番目はtimespec構造体で、秒とナノ秒単位の使用済みプロセッサ時間で設定されます。 ほとんどのオペレーティングシステムでは、プログラムをlibrtにリンクする必要があります。


ただし、クロスプラットフォームコードでこの関数を使用するのを難しくするいくつかの微妙な点があります。



OS使用するID
エクスCLOCK_PROCESS_CPUTIME_ID
BSDCLOCK_VIRTUAL
HP-UXCLOCK_VIRTUAL
LinuxCLOCK_PROCESS_CPUTIME_ID
SolarisCLOCK_PROCESS_CPUTIME_ID


実際には、これらすべての微妙な理由により、 clock_gettime( )を使用するには、 #ifdefを使用した多くのチェックと、機能しない場合に別の関数に切り替える機能が必要です。


Clock_gettime()可用性: AIX、BSD、Cygwin、HP-UX、Linux、およびSolaris。 ただし、BSDおよびHP-UXのクロックIDは非標準です。


Clock_getres()は利用可能です: AIX、BSD、Cygwin、HP-UX、およびLinux、しかしSolarisは動作しません。


clock_getcpuclockid()の可用性: AIXおよびCygwin、Linuxでは無効ではありません。


プロセッサー時間の取得:


 #include <unistd.h> #include <time.h> ... #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) clockid_t id; struct timespec ts; #if _POSIX_CPUTIME > 0 /* Clock ids vary by OS. Query the id, if possible. */ if ( clock_getcpuclockid( 0, &id ) == -1 ) #endif #if defined(CLOCK_PROCESS_CPUTIME_ID) /* Use known clock id for AIX, Linux, or Solaris. */ id = CLOCK_PROCESS_CPUTIME_ID; #elif defined(CLOCK_VIRTUAL) /* Use known clock id for BSD or HP-UX. */ id = CLOCK_VIRTUAL; #else id = (clockid_t)-1; #endif if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 ) return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; #endif 

getrusage()


すべてのUNIXライクなオペレーティングシステムでは、 getrusage()関数は、現在のプロセスで使用されているプロセッサ時間を取得する最も信頼できる方法です。 この関数は、秒およびマイクロ秒単位の時間でrusage構造満たします。 ru_utimeフィールドにはユーザーモードで費やされた時間が含まれ、 ru_stimeフィールドはプロセスに代わってシステムモードになります。


注:一部のオペレーティングシステムでは、64ビットが広くサポートされる前に、32ビット値を返すgetrusage64( )関数と、64ビット値を返すgetrusage64( )関数が定義されていgetrusage( ) 。 現在、 getrusage( )は64ビット値を返し、 getrusage64( )は非推奨です。


Getrusage()の可用性: AIX、BSD、Cygwin、HP-UX、Linux、OSX、およびSolaris。


プロセッサー時間の取得:


 #include <sys/resource.h> #include <sys/times.h> ... struct rusage rusage; if ( getrusage( RUSAGE_SELF, &rusage ) != -1 ) return (double)rusage.ru_utime.tv_sec + (double)rusage.ru_utime.tv_usec / 1000000.0; 

回()


すべてのUNIXライクなオペレーティングシステムでは、廃止されたtimes()関数はtms構造をティック単位のプロセッサ時間で満たし、 sysconf()関数は1秒あたりのティック数を返します。 tms_utimeフィールドにはユーザーモードで費やされた時間が含まれ、 tms_stimeフィールドにはプロセスに代わってシステムモードで費やされた時間が含まれます。


注:古いsysconf( )関数の引数CLK_TCK廃止され、一部のオペレーティングシステムではサポートされない場合があります。 利用可能な場合、 sysconf( )関数は通常、使用時に機能しません。 代わりに_SC_CLK_TCKを使用してください。


Times()の可用性: AIX、BSD、Cygwin、HP-UX、Linux、OSX、およびSolaris。


プロセッサー時間の取得:


 #include <unistd.h> #include <sys/times.h> ... const double ticks = (double)sysconf( _SC_CLK_TCK ); struct tms tms; if ( times( &tms ) != (clock_t)-1 ) return (double)tms.tms_utime / ticks; 

時計()


すべてのUNIXライクなオペレーティングシステムでは、非常に古いclock()関数がプロセスのプロセッサ時間をティックで返し、 CLOCKS_PER_SECマクロが1秒あたりのティック数を返します。


注:返されるプロセッサ時間には、プロセスに代わってユーザーモードおよびシステムモードで費やされた時間が含まれます。


注: CLOCKS_PER_SECは元々プロセッサー固有の値を返すことになっていますが、C ISO C89およびC99標準、Single UNIX Specification、およびPOSIX標準では、 CLOCKS_PER_SEC 1,000,000の固定値が必要であり、マイクロ秒単位で関数の精度が制限されます。 ほとんどのOSはこれらの標準に準拠していますが、FreeBSD、Cygwin、および古いバージョンのOSXは非標準の値を使用しています。


注: AIXおよびSolarisでは、 clock( )関数には、現在のANDプロセスのプロセッサー時間と、親がwait( )system( )またはpclose( )関数のいずれかを実行した終了した子プロセスが含まれます。


注: Windowsでは、 clock()関数がサポートされていますが、プロセッサー時間ではなくリアルタイムを返します。


クロック()の可用性: AIX、BSD、Cygwin、HP-UX、Linux、OSX、およびSolaris。


プロセッサー時間の取得:


 #include <time.h> ... clock_t cl = clock( ); if ( cl != (clock_t)-1 ) return (double)cl / (double)CLOCKS_PER_SEC; 

その他のアプローチ


プロセッサ時間を取得する他のOS固有の方法があります。 Linux、Solaris、および一部のBSDでは、 / proc / [pid] / statを解析してプロセス統計を取得できます。 OSXでは、 proc_pidtaskinfo( )のプライベートproc_pidtaskinfo( ) API関数がプロセス情報を返します。 libproc、 procps、 Sigarなどのオープンライブラリもあります。


UNIXには、 pstopmpstatなど、プロセスのプロセッサ時間を表示するユーティリティがいくつかあります。 timeユーティリティを使用して、コマンドにかかった時間を表示することもできます。


Windowsでは、 タスクマネージャーを使用してCPU使用率を監視できます。


OSXでは、 アクティビティモニターを使用してCPU使用率を監視できます。 XcodeにバンドルされているInstrumentsプロファイリングユーティリティは、CPUの使用率やその他多くのことを監視できます。


ダウンロード



こちらもご覧ください


NadeauSoftware.comの関連記事



インターネット上の記事




Source: https://habr.com/ru/post/J282301/


All Articles