インテル® VTune™ プロファイラー・ユーザーガイド

ユーザー定義の同期 API

インテル® VTune™ プロファイラーは、Windows* および POSIX* API のほとんどをサポートしますが、独自の同期構造を定義すると便利なことがあります。独自に構築された構造は、通常、インテル® VTune™ プロファイラーによって追跡されません。ただし、インテル® VTune™ プロファイラーでは、ユーザー定義の同期構造の統計情報を収集する同期 API が提供されています。

ユーザー定義の同期 API は、再開状態で動作するスレッド単位の関数です。この関数はポーズ状態では動作しません。

同期構造は、一般に一連のシグナルとしてモデル化できます。1 つまたは複数のスレッドが、アクションを続行できることを示す別のスレッドグループからのシグナルを待機することがあります。スレッドがいつシグナルの待機を開始したか、そしていつシグナルが発生したかを追跡することで、同期 API はユーザー定義の同期オブジェクトを取得して、コードの理解に役立つ情報を提供することができます。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 プリミティブは、cancel プリミティブまたは acquired プリミティブとペアにする必要があります。

ユーザー定義の同期構造には任意の数の同期オブジェクトを含めることができます。各同期オブジェクトは、ユーザー定義の同期 API がオブジェクトの追跡に使用する固有のメモリーハンドルから起動する必要があります。オブジェクトが一意のメモリーポインターを使用する限り、ユーザー定義の同期 API を使用して、同時に複数の同期オブジェクトを追跡できます。これは、Windows* API の WaitForMultipleObjects 関数に似たモデル・オブジェクトであると考えることができます。同期オブジェクトのグループから、さらに複雑な同期構造を作成できます。ただし、誤った動作を引き起こす可能性があるため、異なるユーザー定義の同期構造をインターレースすることは推奨できません。

API 使用のヒント

ユーザー定義の同期 API は、コード内にプリミティブを適切に配置する必要があります。ユーザー定義の同期 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);

ロックを取得した後、スレッドがロックを解放する前に releasing API を呼び出す必要があります。次の例は、releasing 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);
        // 実際にロックを解放するコードをここに記述します
    }
}

このクリティカル・セクションの例は、ユーザー定義の同期プリミティブを作成します。次のことに注意して例を参照してください。

使用例: ユーザーレベルの同期バリア

バリアなど上位レベルの構造も同期 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);
        // チームフラグを待機します
        __ itt_sync_acquired((void *) &teamflag);
    }
}

次のことに注意して例を参照してください。

関連情報