Javaでの比較とスワップのセマンティクスは何ですか? つまり、異なるスレッド間のアトミック整数インスタンスの特定のメモリ位置への順序付きアクセスを保証するだけの比較およびスワップ メソッドですかAtomicInteger
、それともメモリ内のすべての位置への順序付きアクセスを保証しますか。つまり、あたかも揮発性であるかのように動作します。 (メモリーフェンス)。
ドキュメントから:
weakCompareAndSet
変数をアトミックに読み取り、条件付きで書き込みますが、先行発生順序付けは作成しません。そのため、 のターゲット以外の変数の前後の読み取りおよび書き込みに関して保証はありませんweakCompareAndSet
。compareAndSet
などの他のすべての読み取りおよび更新操作にgetAndIncrement
は、揮発性変数の読み取りと書き込みの両方のメモリ効果があります。
compareAndSet
揮発性変数であるかのように機能するAPI ドキュメントから明らかです。ただし、weakCompareAndSet
特定のメモリの場所を変更するだけです。したがって、そのメモリ位置が単一のプロセッサのキャッシュ専用である場合、weakCompareAndSet
は通常の よりもはるかに高速であると考えられますcompareAndSet
。
これを尋ねているのは、1 から 8 までthreadnum
のさまざまなスレッドを実行して次のメソッドのベンチマークを行ったからです (コードは静的にコンパイルされた JVM 言語である Scala で記述されていますが、その意味とバイトコード変換の両方がこの場合の Java のそれ - この短いスニペットは明確なはずです):threadnum
totalwork=1e9
val atomic_cnt = new AtomicInteger(0)
val atomic_tlocal_cnt = new java.lang.ThreadLocal[AtomicInteger] {
override def initialValue = new AtomicInteger(0)
}
def loop_atomic_tlocal_cas = {
var i = 0
val until = totalwork / threadnum
val acnt = atomic_tlocal_cnt.get
while (i < until) {
i += 1
acnt.compareAndSet(i - 1, i)
}
acnt.get + i
}
def loop_atomic_weakcas = {
var i = 0
val until = totalwork / threadnum
val acnt = atomic_cnt
while (i < until) {
i += 1
acnt.weakCompareAndSet(i - 1, i)
}
acnt.get + i
}
def loop_atomic_tlocal_weakcas = {
var i = 0
val until = totalwork / threadnum
val acnt = atomic_tlocal_cnt.get
while (i < until) {
i += 1
acnt.weakCompareAndSet(i - 1, i)
}
acnt.get + i
}
4 つのデュアル 2.8 GHz コアと 2.67 GHz 4 コア i7 プロセッサを搭載した AMD 上。JVM は Sun Server Hotspot JVM 1.6 です。結果は、パフォーマンスの違いを示していません。
仕様: AMD 8220 4x デュアルコア @ 2.8 GHz
テスト名: loop_atomic_tlocal_cas
- スレッド番号: 1
実行時間: (最後の 3 つを表示) 7504.562 7502.817 7504.626 (平均 = 7415.637 分 = 7147.628 最大 = 7504.886 )
- スレッド番号: 2
実行時間: (最後の 3 つを表示) 3751.553 3752.589 3751.519 (平均 = 3713.5513 分 = 3574.708 最大 = 3752.949 )
- スレッド番号: 4
実行時間: (最後の 3 つを表示) 1890.055 1889.813 1890.047 (平均 = 2065.7207 分 = 1804.652 最大 = 3755.852 )
- スレッド番号: 8
実行時間: (最後の 3 つを表示) 960.12 989.453 970.842 (平均 = 1058.8776 分 = 940.492 最大 = 1893.127 )
テスト名: loop_atomic_weakcas
- スレッド番号: 1
実行時間: (最後の 3 つを表示) 7325.425 7057.03 7325.407 (平均 = 7231.8682 分 = 7057.03 最大 = 7325.45 )
- スレッド番号: 2
実行時間: (最後の 3 つを表示) 3663.21 3665.838 3533.406 (平均 = 3607.2149 分 = 3529.177 最大 = 3665.838 )
- スレッド番号: 4
実行時間: (最後の 3 つを表示) 3664.163 1831.979 1835.07 (平均 = 2014.2086 分 = 1797.997 最大 = 3664.163 )
- スレッド番号: 8
実行時間: (最後の 3 つを表示) 940.504 928.467 921.376 (平均 = 943.665 分 = 919.985 最大 = 997.681 )
テスト名: loop_atomic_tlocal_weakcas
- スレッド番号: 1
実行時間: (最後の 3 つを表示) 7502.876 7502.857 7502.933 (平均 = 7414.8132 分 = 7145.869 最大 = 7502.933 )
- スレッド番号: 2
実行時間: (最後の 3 つを表示) 3752.623 3751.53 3752.434 (平均 = 3710.1782 分 = 3574.398 最大 = 3752.623 )
- スレッド番号: 4
実行時間: (最後の 3 つを表示) 1876.723 1881.069 1876.538 (平均 = 4110.4221 分 = 1804.62 最大 = 12467.351 )
- スレッド番号: 8
実行時間: (最後の 3 つを表示) 959.329 1010.53 969.767 (平均 = 1072.8444 分 = 959.329 最大 = 1880.049 )
仕様: Intel i7 クアッドコア @ 2.67 GHz
テスト名: loop_atomic_tlocal_cas
- スレッド番号: 1
実行時間: (最後の 3 つを表示) 8138.3175 8130.0044 8130.1535 (平均 = 8119.2888 分 = 8049.6497 最大 = 8150.1950 )
- スレッド番号: 2
実行時間: (最後の 3 つを表示) 4067.7399 4067.5403 4068.3747 (平均 = 4059.6344 分 = 4026.2739 最大 = 4068.5455 )
- スレッド番号: 4
実行時間: (最後の 3 つを表示) 2033.4389 2033.2695 2033.2918 (平均 = 2030.5825 分 = 2017.6880 最大 = 2035.0352 )
テスト名: loop_atomic_weakcas
- スレッド番号: 1
実行時間: (最後の 3 つを表示) 8130.5620 8129.9963 8132.3382 (平均 = 8114.0052 分 = 8042.0742 最大 = 8132.8542 )
- スレッド番号: 2
実行時間: (最後の 3 つを表示) 4066.9559 4067.0414 4067.2080 (平均 = 4086.0608 分 = 4023.6822 最大 = 4335.1791 )
- スレッド番号: 4
実行時間: (最後の 3 つを表示) 2034.6084 2169.8127 2034.5625 (平均 = 2047.7025 分 = 2032.8131 最大 = 2169.8127 )
テスト名: loop_atomic_tlocal_weakcas
- スレッド番号: 1
実行時間: (最後の 3 つを表示) 8132.5267 8132.0299 8132.2415 (平均 = 8114.9328 分 = 8043.3674 最大 = 8134.0418 )
- スレッド番号: 2
実行時間: (最後の 3 つを表示) 4066.5924 4066.5797 4066.6519 (平均 = 4059.1911 分 = 4025.0703 最大 = 4066.8547 )
- スレッド番号: 4
実行時間: (最後の 3 つを表示) 2033.2614 2035.5754 2036.9110 (平均 = 2033.2958 分 = 2023.5082 最大 = 2038.8750 )
上記の例のスレッド ローカルが同じキャッシュ ラインで終了する可能性はありますが、通常の CAS とその脆弱なバージョンとの間に目に見えるパフォーマンスの違いはないように思えます。
これは、実際には、弱い比較とスワップが本格的なメモリ フェンスとして機能する、つまり、揮発性の変数であるかのように機能することを意味する可能性があります。
質問: この観察は正しいですか? また、弱い比較と設定が実際に高速な既知のアーキテクチャまたは Java ディストリビューションはありますか? そうでない場合、そもそも弱い CAS を使用する利点は何ですか?