7
  • ここに、CASを使用してアトミックインクリメント操作を実行しようとするJavaおよびCコードがあります。
  • long変数を0から500,000,000にインクリメントします。
  • C:所要時間:7300ms
  • Java:所要時間:2083ms
  • 誰かがこれらの結果を再確認できますか?信じられないから。
  • ありがとう

Javaコード:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class SmallerCASTest {

    public static void main(String[] args){
        final long MAX = 500l * 1000l * 1000l;
        final AtomicLong counter = new AtomicLong(0);

        long start = System.nanoTime();
        while (true) {
            if (counter.incrementAndGet() >= MAX) {
                break;
            }
        }

        long casTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
        System.out.println("Time Taken=" + casTime + "ms");
    }

}

Cコード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NITER 500000000


int main (){
   long val = 0;     
   clock_t starttime = clock ();
    while (val < NITER){
      while (1){
        long current = val;
        long next = current+1;
        if ( __sync_bool_compare_and_swap (&val, current, next))
            break;
      }
     } 
   clock_t castime = (clock()-starttime)/ (CLOCKS_PER_SEC / 1000);
   printf ("Time taken : %d ",castime);
}

run.sh

#!/bin/bash

gcc -O3 test.c -o test.o
echo -e "\nC"
./test.o
javac SmallerCASTest.java
echo -e "\nJava"
java SmallerCASTest

その他の情報:

System : Linux XXXXXXXXX #1 SMP Thu Mar 22 08:00:08 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

gcc --version:
 gcc (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)

java -version: 
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode)
4

3 に答える 3

5

私はあなたが期待したと確信しているように、あなたはリンゴとオレンジを比較しています. バージョンは、私がフォームで呼び出すものを使用している間java、失敗時に再試行する真の CASです。Cjavasynchronized

詳細については、この質問を参照してください。

それが言うところの物語をサポートするために、その質問に対するこの回答を参照してくださいA full memory barrier is created when this function is invoked。つまり、Javaの用語では、これはsynchronized呼び出しです。

_compare_and_swapを、同等の Java を使用するのと同じ方法で使用してみてくださいAtomicLong。つまり、値が希望する値に変わるまで関数をスピンします。

追加した:

Javaに相当する決定的なC++を見つけることはできませんAtomicLongが、それが存在しないという意味ではありません。基本的に、AtomicLong任意のスレッドがいつでも an を変更でき、そのうちの 1 つだけが成功します。ただし、変更は一貫しています。つまり、変更はいずれかのスレッドによる変更の結果であり、2 つのスレッドの組み合わせではありません。スレッド A が値を 0xffff0000 (または同等の 64 ビット数) に変更しようとしたときに、スレッド B が 0x0000ffff (同上) に変更しようとした場合、結果は2つの値のいずれかなります。もちろん、3 番目のスレッドが関与します)。

基本的に、 にはこれ以外の同期AtomicLongまったくありません。

于 2012-10-09T12:16:51.973 に答える
2

EDIT確かに、ご指摘のとおり、java は CAS 操作を使用して incrementAndGet を実装しているようです。

私のテストでは、C バージョンと Java バージョンのパフォーマンスはほぼ同等であることが示唆されているようです (Java または C コンパイラが管理する残りの部分の最適化ではなく、時間のかかる部分がアトミックであるため、これは理にかなっています)。

したがって、私のマシン (Xeon X3450) では、Java バージョンは ~4700 ミリ秒、C バージョンは ~4600 ミリ秒、__sync_add_and_fetch() を使用する C バージョンは ~3800 ミリ秒かかります (すべてのアトミック操作を実装する代わりに、ここで Java を改善できることを示唆しています)。 CAS の上部)。

ジャバのバージョンは


java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.4) (6b24-1.11.4-1ubuntu0.10.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)

GCC は 4.4.3、x86_64 です。

OSはUbuntu 10.04 x86_64です。

したがって、あなたのテストでは何か怪しいとしか思えません。

于 2012-10-09T11:57:50.180 に答える
0

Javaがすごいから?

Java バージョンでは、ループごとに 4ns かかります。それはほぼ正しいです。非競合 CAS は、実際には CPU ローカル操作であり、非常に高速である必要があります。(編集: おそらく 4ns 高速ではありません!)

Java は積極的な実行時最適化によってその速度を達成します。コードはインライン化され、ほんの数個の機械語命令になります。つまり、アセンブリで手作業でコーディングできるのと同じくらい高速です。

gcc バージョンが関数呼び出しをインライン化できなかった場合、それはループごとに大きなオーバーヘッドになります。

于 2012-10-09T17:34:11.333 に答える