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
が非常に多く含まれる理由について詳しく説明します。
/* * 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_gettime | GetProcessTimes | 取り返し | 回 |
---|---|---|---|---|---|
エクス | はい | はい | はい | はい | |
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( )
( AIX 、 BSD 、 HP-UX 、 Linux、およびSolarisのマニュアルを参照)が最も正確なプロセッサー時間値を提供します。 関数の最初の引数は「clock id」を選択し、2番目はtimespec
構造体で、秒とナノ秒単位の使用済みプロセッサ時間で設定されます。 ほとんどのオペレーティングシステムでは、プログラムをlibrtにリンクする必要があります。
ただし、クロスプラットフォームコードでこの関数を使用するのを難しくするいくつかの微妙な点があります。
- この関数は、POSIX標準のオプション部分であり、
_POSIX_TIMERS
<unistd.h>
0より大きい値で定義されている場合にのみ使用可能です。これまで、AIX、BSD、HP-UX、Linux、およびSolarisはこの関数をサポートしていますが、OSXはサポートしていません。 -
clock_gettime( )
関数によって設定されたtimespec
構造は、ナノ秒単位で時間を格納できますが、クロックの精度はオペレーティングシステムやシステムによって異なります。 clock_getres()関数は、必要に応じて時計の精度を返します。 この関数も、POSIX標準のオプション部分であり、_POSIX_TIMERS
ゼロより大きい場合にのみ使用できます。 現時点では、AIX、BSD、HP-UX、Linux、およびSolarisがこの機能を提供していますが、Solarisでは機能しません。 - POSIX標準は、
CLOCK_PROCESS_CPUTIME_ID
を含むいくつかの標準クロックID値の名前を定義して、プロセスのプロセッサー時間を取得します。 ただし、今日のBSDとHP-UXはこのIDを持たず、代わりにプロセッサ時間用に独自のIDCLOCK_VIRTUAL
を定義します。 さらに混乱させるために、Solarisはこれらの両方を定義していますが 、 プロセスではなくスレッドのプロセッサ時間にCLOCK_VIRTUAL
を使用しています 。
OS | 使用するID |
---|---|
エクス | CLOCK_PROCESS_CPUTIME_ID
|
BSD | CLOCK_VIRTUAL
|
HP-UX | CLOCK_VIRTUAL
|
Linux | CLOCK_PROCESS_CPUTIME_ID
|
Solaris | CLOCK_PROCESS_CPUTIME_ID
|
- 上記で宣言した定数の1つを使用する代わりに、 clock_getcpuclockid()関数は選択したプロセスのタイマーを返します。 プロセス0を使用すると、現在のプロセスのプロセッサ時間を取得できます。 ただし、これはPOSIX標準の別のオプション部分であり、
_POSIX_CPUTIME
0より大きい場合にのみ使用可能です。これまでのところ、AIXとLinuxのみがこの関数を提供していますが、Linuxインクルードファイルは_POSIX_CPUTIME
を定義せず、関数は信頼性のない互換性のないPOSIX結果を返します。 -
clock_gettime( )
関数は、プロセッサ時間レジスタを使用して実装できます。 マルチプロセッサシステムでは、プロセスがプロセッサからプロセッサに転送された場合、関数が誤った値を返す可能性があるため、個々のプロセッサの時間の認識がわずかに異なる場合があります。 Linuxで、Linuxでのみ、これはclock_getcpuclockid( )
が非POSIXエラーをerrno
、errno
をENOENT
errno
する場合に検出できます。 ただし、上記のように、Linuxでは、clock_getcpuclockid( )
信頼できません。
実際には、これらすべての微妙な理由により、 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には、 ps 、 top 、 mpstatなど、プロセスのプロセッサ時間を表示するユーティリティがいくつかあります。 timeユーティリティを使用して、コマンドにかかった時間を表示することもできます。
Windowsでは、 タスクマネージャーを使用してCPU使用率を監視できます。
OSXでは、 アクティビティモニターを使用してCPU使用率を監視できます。 XcodeにバンドルされているInstrumentsプロファイリングユーティリティは、CPUの使用率やその他多くのことを監視できます。
ダウンロード
- getCPUTime.cは、上記の関数をCで実装します。任意のCコンパイラでコンパイルし、利用可能なシステム上でlibrtにリンクします。 コードはCreative Commons Attribution 3.0 Unported Licenseの下でライセンスされています。
こちらもご覧ください
NadeauSoftware.comの関連記事
- C / C ++のヒント:ベンチマークのためにリアルタイムの経過時間を測定する方法では、I / Oまたはユーザー入力に費やされた時間を含むコードの経過時間を測定するためにリアルタイムを取得する方法を説明します。
- C / C ++のヒント:コンパイラーの定義済みマクロを使用してオペレーティングシステムを検出する方法では、OS固有のコードで
#ifdef
マクロを使用する方法について説明します。 この記事では、これらの方法のいくつかを使用して、Windows、OSX、およびUNIXのバリアントを識別します。
インターネット上の記事
- ウィキペディアのプロセッサ時間は、プロセッサ時間とは何かを説明しています。
- GNU.org のCPU Time Inquiryは、古代のclock()関数の使用方法を説明しています。
- 現在のプロセス(C ++およびC#)のCPU使用率を判別すると、Windowsでのプロセッサー時間およびその他の統計を取得するためのコードと説明が提供されます。
- Kernel.orgのPosixオプションでは、_POSIX_TIMERSや_POSIX_CPUTIMEなどのオプションのPOSIX機能と定数について説明しています。