Intel Inspector がスピンロック実装でデータ競合を報告する
-
16-09-2019 - |
質問
Windows の Interlocked 関数を使用して非常に単純なスピンロックを作成し、デュアルコア CPU (変数をインクリメントする 2 つのスレッド) でテストしました。
プログラムは問題なく動作しているように見えますが (毎回同じ結果が得られますが、同期を使用しない場合はこの限りではありません)、 インテル並列インスペクター で競合状態が発生していると言います。 値 += j (以下のコードを参照してください)。SpinLock の代わりにクリティカル セクションを使用すると、警告が表示されなくなります。
SpinLock の実装は正しいかどうか?これは本当に奇妙です。使用される操作はすべてアトミックであり、適切なメモリ バリアがあり、競合状態が発生するはずがないからです。
class SpinLock
{
int *lockValue;
SpinLock(int *value) : lockValue(value) { }
void Lock() {
while(InterlockedCompareExchange((volatile LONG*)lockValue, 1, 0) != 0) {
WaitABit();
}
}
void Unlock() { InterlockedExchange((volatile LONG*)lockValue, 0); }
};
テストプログラム:
static const int THREADS = 2;
HANDLE completedEvents[THREADS];
int value = 0;
int lock = 0; // Global.
DWORD WINAPI TestThread(void *param) {
HANDLE completed = (HANDLE)param;
SpinLock testLock(&lock);
for(int i = 0;i < 1000*20; i++) {
for(int j = 0;j < 10*10; j++) {
// Add something to the variable.
testLock.Lock();
value += j;
testLock.Unlock();
}
}
SetEvent(completed);
}
int main() {
for(int i = 0; i < THREADS; i++) {
completedEvents[i] = CreateEvent(NULL, true, false, NULL);
}
for(int i = 0; i < THREADS; i++) {
DWORD id;
CreateThread(NULL, 0, TestThread, completedEvents[i], 0, &id);
}
WaitForMultipleObjects(THREADS, completedEvents, true, INFINITE);
cout<<value;
}
解決
Parallel Inspector のドキュメント データ競争 Windows 上の競合を修正するためにクリティカル セクションまたはミューテックスを使用することを提案しています。この中には、Parallel Inspector がユーザーが発明した他のロック メカニズムを認識する方法を知っていることを示唆するものは何もありません。
新しいロック メカニズムを分析するためのツールは、コード内のあらゆるパスを調べる静的ツールであることが多く、Parallel Inspector のドキュメントではコードを 1 回実行することが示唆されています。
新しいロック機構を試したい場合、学術文献で使用されているのを私が見た中で最も一般的なツールは次のとおりです。 スピンモデルチェッカー. 。それもあります 超能力者, 、これにより状態空間が削減される可能性がありますが、それが同時発生する問題に適用されているかどうかはわかりません。 モビリティワークベンチ パイ計算で問題を解決できれば、分析が得られるでしょう。Intel Parallel Inspector は、これらのツールほど複雑なものではないようですが、ヒューリスティックを使用して一般的に発生する問題をチェックするように設計されています。
他のヒント
私に似たような状況で、他の貧しい人々のために:インテルは含まれており、まさにこの種のものを行うためのライブラリのセットを提供します。これらの材料のために(あなたが表示されます\含まれ、インストールディレクトリの\ lib32がと\ lib64に)インスペクタのインストールディレクトリに確認してください。 (Intelは一貫したリンクを維持については何も気にしないものの、2018年6月の時点で)それらを使用する方法についてのドキュメント:
ます。https://ソフトウェア.intel.com / EN-US /インスペクター・ユーザー・ガイド-WindowsのAPIを-のためのカスタム・同期する
3つの機能があります:
void __itt_sync_acquired(void *addr)
void __itt_sync_releasing(void *addr)
void __itt_sync_destroy(void *addr)
私は次のように実施されるべきであるかなり確信します:
class SpinLock
{
long lockValue;
SpinLock(long value) : lockValue(value) { }
void Lock() {
while(InterlockedCompareExchange(&lockValue, 1, 0) != 0) {
WaitABit();
}
}
void Unlock() { InterlockedExchange(&lockValue, 0); }
};