20

Intel x86 ベースのプラットフォーム (特に、Intel コンパイラを使用して MacOSX 10.4 を実行している Intel ベースの Mac) で C でプログラミングする場合、64 ビット書き込みがアトミックであることが保証されるのはいつですか? 例えば:

unsigned long long int y;
y = 0xfedcba87654321ULL;
/* ... a bunch of other time-consuming stuff happens... */
y = 0x12345678abcdefULL;

y への最初の代入の実行が終了した後に別のスレッドが y の値を調べている場合、値 0xfedcba87654321 または値 0x12345678abcdef のいずれかが表示され、それらの混合ではないようにしたいと考えています。ロックせずに、可能であれば余分なコードなしでこれを行いたいと思います。私の希望は、64 ビット コードをサポートできるオペレーティング システム (MacOSX 10.4) で 64 ビット コンパイラ (64 ビット Intel コンパイラ) を使用する場合、これらの 64 ビット書き込みがアトミックになることです。これは常に真実ですか?

4

8 に答える 8

44

最善の策は、プリミティブから独自のシステムを構築しようとするのを避け、プロファイリング時に実際にホット スポットとして表示されない限り、代わりにロックを使用することです。(あなたが賢くてロックを避けることができると思うなら、そうではありません。それは、私と他のすべての人を含む一般的な「あなた」です。) 少なくともスピンロックを使用する必要があります。spinlock(3)を参照してください。何をするにしても、「独自の」ロックを実装しようとしないでください。あなたはそれを間違えます。

最終的には、オペレーティング システムが提供するロック操作またはアトミック操作を使用する必要があります。このようなことをすべての場合に正確に行うことは、非常に困難です。多くの場合、特定のプロセッサの特定のバージョンのエラッタなどの知識が必要になる場合があります。(「ああ、そのプロセッサのバージョン 2.0 は適切なタイミングでキャッシュ コヒーレンス スヌーピングを実行しませんでした。バージョン 2.0.1 で修正されましたが、2.0 では を挿入する必要があります。」) C の変数にキーワードを平手打ちするだけほとんど常に不十分です。NOPvolatile

Mac OS X では、atomic(3)にリストされている関数を使用して、32 ビット、64 ビット、およびポインター サイズの数量に対してすべての CPU で真にアトミックな操作を実行する必要があります。(ポインターのアトミック操作には後者を使用して、自動的に 32/64 ビット互換になります。) それは、アトミックな比較とスワップ、インクリメント/デクリメント、スピン ロック、またはスタック/キューなどを実行するかどうかに関係なく当てはまります。管理。幸い、spinlock(3)atomic(3)、およびbarrier(3)関数はすべて、Mac OS X でサポートされているすべての CPU で正しく動作するはずです。

于 2008-09-16T23:35:50.803 に答える
13

x86_64 では、Intel コンパイラと gcc の両方が、組み込みのアトミック操作関数をサポートしています。それらのgccのドキュメントは次のとおりです。

Intel コンパイラのドキュメントでも、これらについて説明しています: http://softwarecommunity.intel.com/isn/downloads/softwareproducts/pdfs/347603.pdf (164 ページまたはそのあたり)。

于 2008-09-16T23:26:04.727 に答える
11

Intel のプロセッサ マニュアルのパート 3A - システム プログラミング ガイドの第 7 章によると、クワッドワード アクセスは、Pentium 以降では 64 ビット境界で整列され、非整列 (まだキャッシュ ライン内にある場合) の場合、アトミックに実行されます。 P6 以降。コンパイラが書き込みを変数にキャッシュしようとしないようにするために使用する必要があります。書き込みが適切な順序で行われるようにするために、メモリ フェンス ルーチンを使用する必要がある場合があります。volatile

既存の値に基づいて値を書き込む必要がある場合は、オペレーティング システムのインターロック機能を使用する必要があります (たとえば、Windows には InterlockedIncrement64 があります)。

于 2008-09-16T23:39:53.477 に答える
10

Intel MacOSXでは、組み込みのシステムアトミック操作を使用できます。32ビットまたは64ビット整数のアトミックgetまたはsetは提供されていませんが、提供されているCompareAndSwapから構築できます。さまざまなOSAtomic関数については、XCodeのドキュメントを検索することをお勧めします。以下に64ビットバージョンを作成しました。32ビットバージョンは、同様の名前の関数を使用して実行できます。

#include <libkern/OSAtomic.h>
// bool OSAtomicCompareAndSwap64Barrier(int64_t oldValue, int64_t newValue, int64_t *theValue);

void AtomicSet(uint64_t *target, uint64_t new_value)
{
    while (true)
    {
        uint64_t old_value = *target;
        if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, target)) return;
    }
}

uint64_t AtomicGet(uint64_t *target)
{
    while (true)
    {
        int64 value = *target;
        if (OSAtomicCompareAndSwap64Barrier(value, value, target)) return value;
    }
}

AppleのOSAtomicCompareAndSwap関数は、次の操作をアトミックに実行することに注意してください。

if (*theValue != oldValue) return false;
*theValue = newValue;
return true;

上記の例では、これを使用して、最初に古い値を取得し、次にターゲットメモリの値を交換しようとすることでSetメソッドを作成します。スワップが成功した場合、それはメモリの値がスワップ時の古い値のままであり、スワップ中に新しい値が与えられることを示します(それ自体はアトミックです)。これで完了です。成功しない場合は、他のスレッドが、値を取得してからリセットしようとしたときに、その間の値を変更することによって干渉しています。その場合は、最小限のペナルティでループして再試行できます。

Getメソッドの背後にある考え方は、最初に値を取得できることです(別のスレッドが干渉している場合、実際の値である場合とそうでない場合があります)。次に、値をそれ自体と交換して、最初のグラブがアトミック値と等しいことを確認するだけです。

私はこれを私のコンパイラに対してチェックしていませんので、タイプミスを許してください。

OSXについて具体的に言及しましたが、他のプラットフォームで作業する必要がある場合に備えて、Windowsには多数のInterlocked *関数があり、MSDNドキュメントでそれらを検索できます。それらのいくつかはWindows2000Pro以降で動作し、いくつか(特に64ビット関数のいくつか)はVistaで新しく追加されました。他のプラットフォームでは、GCCバージョン4.1以降には、__ sync_fetch_and_add()などのさまざまな__sync*関数があります。他のシステムでは、アセンブリを使用する必要がある場合があります。いくつかの実装は、HaikuOSプロジェクトのSVNブラウザーのsrc / system / libroot / os/arch内にあります。

于 2008-09-16T23:49:14.093 に答える
6

X86 でアラインされた 64 ビット値をアトミックに書き込む最速の方法は、FISTP を使用することです。位置合わせされていない値については、CAS2 (_InterlockedExchange64) を使用する必要があります。ただし、CAS2 操作は BUSLOCK のために非常に遅いため、アラインメントをチェックして、アラインメントされたアドレスに対して FISTP バージョンを実行する方が高速になることがよくあります。実際、これはインテルのスレッド化ビルディング ブロックがアトミック 64 ビット書き込みを実装する方法です。

于 2009-10-30T19:33:45.640 に答える
3

ISO C (C11) の最新バージョンでは、atomic_store(_explicit). 詳細については、このページなどを参照してください。

アトミックの 2 番目に移植性の高い実装は、既に説明した GCC 組み込み関数です。GCC、Clang、Intel、および IBM コンパイラで完全にサポートされており、前回確認した時点では、Cray コンパイラで部分的にサポートされていることがわかりました。

C11 アトミックの明らかな利点の 1 つは、ISO 標準全体に加えて、より正確なメモリ一貫性の規定をサポートしていることです。私の知る限り、GCCアトミックは完全なメモリバリアを意味します。

于 2015-04-06T22:20:29.023 に答える
2

スレッド間またはプロセス間通信のためにこのようなことをしたい場合は、アトミックな読み取り/書き込み保証以上のものを用意する必要があります。あなたの例では、いくつかの作業が進行中および/または完了したことを示すために書き込まれた値が必要なようです。いくつかのことを行う必要がありますが、そのすべてが移植可能であるとは限りません。コンパイラーが実行したい順序で処理を行い (volatile キーワードがある程度役立つ場合があります)、メモリの一貫性を確保するためです。最新のプロセッサとキャッシュは、コンパイラに知られていない順序で作業を実行する可能性があるため、実行したいように見えることを実行するには、何らかのプラットフォーム サポート (つまり、ロックまたはプラットフォーム固有のインターロック API) が本当に必要です。

「メモリ フェンス」または「メモリ バリア」は、調査する必要がある用語です。

于 2008-09-16T23:41:31.930 に答える
1

GCC には、アトミック操作用の組み込み関数があります。他のコンパイラでも同様のことができると思います。アトミック操作をコンパイラに頼らないでください。コンパイラーにそうしないように明示的に指示しない限り、最適化はほぼ確実に、明らかにアトミックな操作でさえ非アトミックな操作にするリスクを冒します。

于 2008-09-16T23:18:28.880 に答える