インテル® VTune™ プロファイラー・ユーザーガイド
インテル® VTune™ プロファイラーは、Windows* および POSIX* API のほとんどをサポートしますが、独自の同期構造を定義すると便利なことがあります。独自にビルドされた構造は、通常、インテル® VTune™ プロファイラーによって追跡されません。ただし、インテル® VTune™ プロファイラーでは、ユーザー定義の同期構造の統計情報を収集する同期 API が提供されます。
ユーザー定義同期 API は、再開状態で動作するスレッドごとの関数です。この関数はポーズ状態では動作しません。
同期構造は、一般に一連のシグナルとしてモデル化できます。1 つまたは複数のスレッドが、別のスレッドグループからのシグナルで、何らかのアクションを続行することがあります。スレッドがいつシグナルの待機を開始したか、そしていつシグナルが発生したかを追跡することで、同期 API はユーザー定義の同期オブジェクトを取得して、コードの理解に役立つ情報を提供することができます。API は、一連のプリミティブとメモリーハンドルを使用して、ユーザー定義の同期オブジェクトに関連する統計を収集します。
ユーザー定義同期 API は、スレッド化解析タイプと連携します。
次の表は、Windows* または Linux* オペレーティング・システムで利用可能なユーザー定義の同期 API プリミティブについて説明します。
使用するプリミティブ |
説明 |
---|---|
void __itt_sync_create (void *addr, const __itt_char *objtype, const __itt_char *objname, int attribute) |
char または Unicode* 文字列を使用する同期オブジェクトの作成を登録します。 |
void __itt_sync_rename (void *addr, const __itt_char *name) | 作成後、char または Unicode* 文字列を使用して同期オブジェクトに名前を割り当てます。 |
void __itt_sync_destroy (void *addr) |
破棄されたオブジェクトのライフタイムを追跡します。 |
· void __itt_sync_prepare (void *addr) |
ユーザー定義同期オブジェクトでスピンループに入ります。 |
void __itt_sync_cancel (void *addr) |
スピン・オブジェクトを取得せずにスピンループを出ます。 |
void __itt_sync_acquired (void *addr) |
スピンループの正常終了を定義します (同期オブジェクトの取得)。 |
void __itt_sync_releasing (void *addr) |
同期オブジェクトを解放するコードを開始します。このプリミティブは、ロック解放呼び出しの前に呼び出されます。 |
各 API は単一の引数、アドレス (addr) を持ちます。値ではなくアドレスを指定して、2 つ以上の異なるカスタム同期オブジェクトを区別します。インテル® VTune™ プロファイラーは、アドレスごとに個別のカスタム・オブジェクトを追跡できます。そのため、同じカスタム・オブジェクトで異なるコード領域へのアクセスを保護するには、同じ addr パラメーターを使用します。
コードに正しく埋め込まれていると、プリミティブはコードの同期タイプをインテル® VTune™ プロファイラーに通知します。各 prepare プリミティブは、キャンセル・プリミティブまたは取得プリミティブとペアで使用する必要があります。
ユーザー定義同期構造には任意の数の同期オブジェクトを含めることができます。同期オブジェクトは、ユーザー定義同期 API がオブジェクトの追跡に使用する固有のメモリーハンドルから起動する必要があります。オブジェクトが一意のメモリーポインターを使用する限り、ユーザー定義同期 API を使用して、同時に複数の同期オブジェクトを追跡できます。これは、Windows* API の WaitForMultipleObjects 関数に似たモデル・オブジェクトであると考えることができます。同期オブジェクトのグループから、さらに複雑な同期構造を作成できます。ただし、誤った動作を引き起こす可能性があるため、異なるユーザー定義同期構造をインターレースすることは推奨できません。
ユーザー定義同期 API は、コード内に適切に配置する必要があります。ユーザー定義同期 API を適切に使用するには次のガイドラインに従ってください。
同期オブジェクトへのアクセスを取得するコードの直前に prepare プリミティブを配置します。
コードが同期オブジェクトを待機しなくなった直後に、cancel または acquired プリミティブを配置します。
解放プリミティブは、同期オブジェクトを保持しないことをコードが通知する前に使用する必要があります。
複数のオブジェクトを待機する構造をシミュレートするため複数の prepare プリミティブを使用する場合、prepare プリミティブのグループに関連するオブジェクトの最後の cancel または acquired プリミティブによって、構造の動作が cancel または acquired であるかが決定されます。
prepare プリミティブと acquired プリミティブ間の時間は、影響時間であると見なすことができます。
プロセッサーがブロックしなくても、prepare プリミティブと cancel プリミティブ間の時間はブロック時間と見なされます。
ユーザー定義同期 API の使い方を誤ると、統計データが不正確になります。
prepare API は、現在のスレッドがメモリー位置のシグナルを待機していることをインテル® VTune™ プロファイラーに通知します。この呼び出しは、ユーザー同期構造を呼び出す前に行わなければなりません。prepare API は、acquired または cancel API 呼び出しとペアである必要があります。
次のコード例は、ユーザー定義のスピン待機構造と組み合わせて prepare および acquired API を使用する方法を示します。
long spin = 1;
. . ..
....
__itt_sync_prepare((void *) &spin );
while(ResourceBusy);
// スピン待機
__itt_sync_acquired((void *) &spin );
cancel API は、スレッドが同期構造をテストし、別のスレッドからのシグナルを待機する間に、他のワークを実行するなどのシナリオに適用できます。次のコード例を参照してください。
long spin = 1;
....
....
__itt_sync_prepare((void *) &spin );
while(ResourceBusy)
{
__itt_sync_cancel((void *) &spin );
//
// ワークを記述
//
.....
.....
//
// ワークが完了すると、この構造はロック変数をテストし取得を試みます
// これを行う前に、prepare API を呼び出す必要があります。
//
__itt_sync_prepare((void *) &spin );
}
__itt_sync_acquired((void *) &spin);
ロックを取得した後、スレッドはロックを解放する前に解放 API を呼び出す必要があります。次の例は、リリース API の使用方法を示します。
long spin = 1;
....
....
__itt_sync_releasing((void *) &spin );
// コードはここでリソースを解放する必要があります
次のコード例は、ユーザー定義同期 API を使用して追跡可能なクリティカル・セクションを作成する方法を示します。
CSEnter()
{
__itt_sync_prepare((void*) &cs);
while(LockIsUsed)
{
if(LockIsFree)
{
// 実際にロックを取得するコードを記述
__itt_sync_acquired((void*) &cs);
}
if(timeout)
{
__itt_sync_cancel((void*) &cs );
}
}
}
CSLeave()
{
if(LockIsMine)
{
__itt_sync_releasing((void*) &cs);
// 実際にロックを解放するコードを記述
}
}
このクリティカル・セクションの例は、ユーザー定義同期プリミティブを作成します。次のことに注意して例を参照してください。
それぞれの prepare プリミティブは、acquired プリミティブまたは cancel プリミティブとペアになっています。
prepare プリミティブは、ユーザーコードがユーザーロックを待機する直前に配置します。
acquired プリミティブは、ユーザーコードがユーザーロックを取得した直後に配置します。
解放プリミティブは、ユーザーコードがユーザーロックを解放する直前に配置します。インテル® VTune™ プロファイラーは、スレッドがロックを解放したことを認識する前に、他のスレッドが acquired プリミティブを呼び出さないようにします。
バリアなど上位レベルの構造も同期 API を使用して容易にモデル化できます。次のコード例は同期 API を使用して追跡されるバリア構造を作成する方法を示します。
Barrier()
{
teamflag = false;
__itt_sync_releasing((void *) &counter);
InterlockedIncrement(&counter); // OS とコンパイラーに適したアトミック・インクリメントを使用
if( counter == thread count )
{
__itt_sync_acquired((void *) &counter);
__itt_sync_releasing((void *) &teamflag);
teamflag = true;
counter = 0;
}
else
{
__ itt_sync_prepare((void *) &teamflag);
Wait for team flag
__ itt_sync_acquired((void *) &teamflag);
}
}
次のことに注意して例を参照してください。
このバリアコードには 2 つの同期オブジェクトがあります。counter オブジェクトは、各スレッドがバリアに入ったことを示すため、すべてのスレッドから最後のスレッドへギャザーのようなシグナル通知を行う際に使用されます。最後のスレッドがバリアに到達すると、teamflag オブジェクトを使用してすべてのスレッドに継続が可能であることを通知します。
各スレッドはバリアに入ると __itt_sync_releasing を呼び出して、counter をインクリメントして最後のスレッドに通知することをインテル® VTune™ プロファイラーに伝えます。
バリアに入る最後のスレッドは、__itt_sync_acquired を呼び出して、その他のスレッドからの通知を正常に受信したことをインテル® VTune™ プロファイラーに伝えます。
バリアに入る最後のスレッドは、__itt_sync_releasing を呼び出して、teamflag を設定してその他のスレッドにバリアの終了を通知することをインテル® VTune™ プロファイラーに伝えます。
最後のスレッドを除くほかのスレッドは、__itt_sync_prepare プリミティブを呼び出して、最後のスレッドからの teamflag シグナルを待機していることをインテル® VTune™ プロファイラーに伝えます。
最後に、バリアを出る前に、各スレッドは __itt_sync_acquired プリミティブを呼び出して、バリア終了シグナルを正常に受信したことをインテル® VTune™ プロファイラーに伝えます。