为什么我测试golang goroutine比java Thread

时间:2016-05-27 05:27:34

标签: java multithreading go goroutine

======================编辑2016 05 27 16:55解决================= ==

这个问题解决了!感谢@Paul Hankin,你是对的! Java擅长做"对于(){i = i + 1}",所以当我将代码更改为" for(){i = i + i}"时,java输。

(PS:使用Java ExecutorService真的让java结果很好,但仍然不如goroutine,这里没有ExecutorService示例)

Java代码:

import java.util.ArrayList;
import java.util.List;


public class Test {
    public static void main(String args[]) throws InterruptedException {
        List<Thread> threads = new ArrayList<Thread>();


        for(int i = 0; i < 10; i ++) {
            threads.add(new Thread(new RunableAdd()));
        }

        long timeStart = System.currentTimeMillis();

        for(Thread t: threads) {
            t.start();
        }


        for(Thread t: threads) {
            t.join();
        }


        System.out.println("Time Use : " + (System.currentTimeMillis() - timeStart) + "ms");
    }


}

class RunableAdd implements Runnable {
    @Override
    public void run() {
        long i = 1;
        while(true) {
            i += i;
            if (i > 1000 * 1000* 1000) {
                return;
            }
        }
    }
}

Golang代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    chanSignal := make(chan bool)

    startTime := time.Now()

    goroutineNum := 10

    for i := 0; i < goroutineNum; i++ {
        go func() {
            j := 1
            for {
                j = j + j
                if j > 1000*1000*1000 {
                    chanSignal <- true
                    return
                }
            }
        }()
    }

    for i := 0; i < goroutineNum; i++ {
        <-chanSignal
    }

    fmt.Println(time.Since(startTime))
}

结果:

Thread / goroutine num:10

java : Time Use : 4ms
golang : 19.105µs

Thread / goroutine num:100

java : Time Use : 27ms
golang : 180.272µs

Thread / goroutine num:1000

java: Time Use : 512ms
golang : 1.565521ms

事实证明golang很好,java很擅长&#34; i = i + 1&#34;,

在goroutine num pluse 10中,时间成本低于10倍。

===============================结束=============== =====================

我今天用java线程测试golang goroutine,我认为golang goroutine应该比java Thread快,但我的测试结果显示java线程获胜。

环境: java 1.8 golang 1.6

cpu是4核心

$ cat /proc/cpuinfo | grep 'core id'
core id     : 0
core id     : 0
core id     : 1
core id     : 1

Java代码:

import java.util.ArrayList;
import java.util.List;


public class Test {
    public static void main(String args[]) throws InterruptedException {
        List<Thread> threads = new ArrayList<Thread>();


        for(int i = 0; i < 100; i ++) {
            threads.add(new Thread(new RunableAdd()));
        }

        long timeStart = System.currentTimeMillis();

        for(Thread t: threads) {
            t.start();
        }


        for(Thread t: threads) {
            t.join();
        }


        System.out.println("Time Use : " + (System.currentTimeMillis() - timeStart) + "ms");
    }


}

class RunableAdd implements Runnable {
    @Override
    public void run() {
        int i = 0;
        while(true) {
            i ++;
            if (i == 10 * 1000* 1000) {
                return;
            }
        }
    }
}

golang代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    chanSignal := make(chan bool)

    startTime := time.Now()

    goroutineNum := 100

    for i := 0; i < goroutineNum; i++ {
        go func() {
            j := 0
            for {
                if j == 10*1000*1000 {
                    chanSignal <- true
                    return
                } else {
                    j = j + 1
                }
            }
        }()
    }

    for i := 0; i < goroutineNum; i++ {
        <-chanSignal
    }

    fmt.Println(time.Since(startTime))
}

结果是:

当我设置线程数10:

Java : Time Use : 18ms
golang : 50.952259ms

线程数100:

Java : Time Use : 88ms
golang : Time Use : 458.239685ms

线程数1000:

Java : Time Use : 1452ms
golang : 4.701811465s

Java Thread真的比golang goroutine快吗?或者我的程序做错了什么?

2 个答案:

答案 0 :(得分:3)

基准很难做到正确,当你测量其他东西时,很容易认为你正在测量一件事(goroutines /线程的成本)。我认为这是在这里发生的事情。

例如,thread / goroutines中的内部循环在两个程序中是相同的。将goroutine重写为更像java代码:

for j := 0; j != 10*1000*1000; j++ {}
chanSignal <- true

带来了2倍的加速。

当我在我的机器上运行它时仍然会留下显着的性能差距 - go代码需要663ms,1000 goroutines,java代码55ms,1000个线程。也许正在发生的事情是,JVM在执行了几次后,在线程的run方法中JITting掉了无用的循环。

这里有一些证据表明goroutines的成本并不相关。只是串行执行代码,我的机器上的运行时间为2.55秒:

package main

import (
    "fmt"
    "time"
)

func main() {
    goroutineNum := 1000
    startTime := time.Now()
    for i := 0; i < goroutineNum; i++ {
        for j := 0; j < 1000*1000*10; j++ {
        }
    }
    fmt.Println(time.Since(startTime))
}

这个代码的基于goroutine的版本在4个处理器上运行,长度为663毫秒,仅略高于串行代码所用2.55秒的四分之一(在我的机器上)。所以这是非常有效的并行性,并且goroutine的成本可以忽略不计。

因此大多数情况下,我认为您正在对java的执行效率进行基准测试并执行空循环。对于JIT来说,这似乎是一个完美的用例,您可以在Java代码的出色运行时性能中看到它。

答案 1 :(得分:0)

i = i + i测试是愚蠢的,因为它几乎只执行了24次添加和24次测试。比较,我相信我们都承认我们经常遇到现实世界的问题,只需要48个机器指令即可完成。

而不是Thread,Executors.newFixedThreadPool更接近Java相当于goroutine,尝试热身,你可能得到不同的结果。