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);
}

运行.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,当该C版本使用我在表单中调用java的内容时,会在失败时重试。synchronized

有关更多详细信息,请参阅此问题

请参阅该问题的答案以支持它所说的叙述A full memory barrier is created when this function is invoked,即用Java术语来说,这是一个synchronized电话。

尝试使用_compare_and_swap以与使用它的 java 等效项相同的方式AtomicLong使用,即旋转函数直到值更改为您想要的值。

添加:

我找不到一个明确的 C++ 等价于 java AtomicLong,但这并不意味着没有。本质上,AtomicLong任何线程都可以随时更改 an 并且其中只有一个成功。但是,更改将是一致的,即更改将是一个或另一个线程更改的结果,而不是两者的组合。如果线程 A 尝试将值更改为 0xffff0000(或等效的 64 位数字),而线程 B 尝试更改为 0x0000ffff(同上),则结果将是两个值中的一个,更具体地说,它不会是 0x00000000 或 0xffffffff(除非当然涉及第三个线程)。

本质上,除此之外,anAtomicLong根本没有同步

于 2012-10-09T12:16:51.973 回答
2

编辑确实,正如您所指出的,java 似乎使用 CAS 操作来实现 incrementAndGet。

我的测试似乎表明 C 和 Java 版本具有大致相同的性能(这是有道理的,因为耗时的部分是原子的,而不是 java 或 C 编译器设法完成的其余部分的任何优化)。

所以在我的机器(至强 X3450)上,java 版本需要 ~4700 ms,C 版本 ~4600 ms,使用 __sync_add_and_fetch() 的 C 版本 ~3800 ms(建议可以在此处改进 java 而不是在CAS 的顶部)。

java版本是


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。

操作系统是 Ubuntu 10.04 x86_64。

所以我只能得出结论,在你的测试中有些东西看起来很可疑。

于 2012-10-09T11:57:50.180 回答
0

Because Java is awesome?

The java version takes 4ns for each loop. That is about right. An uncontended CAS is actually a CPU local operation, it should be very fast. (edit: probably not 4ns fast!)

Java achieves that speed by aggressive runtime optimization, the code is inlined and becomes just a couple of machine instructions, i.e. as fast as one can hand-code in assembly.

If the gcc version couldn't inline the function call, that's a big overhead per loop.

于 2012-10-09T17:34:11.333 回答