【问题标题】:Why I test golang goroutine slow than java Thread为什么我测试 golang goroutine 比 java Thread 慢
【发布时间】:2016-09-25 07:41:50
【问题描述】:

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

这个问题解决了!感谢@Paul Hankin,你是对的! Java擅长做“for() {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))
}

结果:

线程/goroutine 数量:10

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

线程/goroutine 数量:100

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

线程/goroutine 数量:1000

java: Time Use : 512ms
golang : 1.565521ms

原来golang好,java好“i = i + 1”,

有趣的是,当goroutine num加10时,时间成本不到10倍。

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

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

环境: 爪哇 1.8 golang 1.6

cpu 是四核

$ 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 快吗?还是我的程序做错了什么?

【问题讨论】:

  • “我认为 golang goroutine 应该比 java 更快” -- 你为什么这么认为?
  • @JimGarrison accrouding to The Go scheduler this page 似乎每个人都说goroutine更轻,创建速度比线程快,成本低于线程,并且当在一个cpu上执行大量线程时,改变cpu成本线程,但是将一个 goroutine 更改为另一个 goroutine 几乎没有任何成本。
  • @Alexniver 没错。您的代码是否证明了这一点?您的代码是否允许 Go 调度程序运行?两个代码的所有同步点是否相同?您的代码是否衡量了更改线程/goroutines 的成本?
  • 在使用空循环和未使用的变量进行基准测试时要小心,现在的编译器倾向于对此进行优化。
  • 请使用System.nanoTime(),因为currentTimeMillis() 不精确,因为the granularity of the value depends on the underlying operating system and may be larger

标签: java multithreading go goroutine


【解决方案1】:

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

例如,线程/goroutines 中的内部循环在两个程序中是不一样的。重写 goroutine 使其更像 java 代码:

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

实现 2 倍加速。

当我在我的机器上运行它时,这仍然留下了很大的性能差距——go 代码需要 663ms 和 1000 个 goroutine,而 java 代码需要 55ms 和 1000 个线程。可能发生的情况是 JVM 在线程的 run 方法中执行了几次后,正在 JITting 中删除无用的循环。

这里有一些证据表明 goroutine 的成本不相关。只需串行执行代码,在我的机器上运行时间为 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 秒(在我的机器上)的四分之一。所以这确实是非常有效的并行性,goroutines 的成本可以忽略不计。

所以我认为你主要是在对 java 和 go 执行空循环的效率进行基准测试。看起来这是 JIT 的完美用例,您可以在 Java 代码的出色运行时性能中看到这一点。

【讨论】:

  • 谢谢!我将“i = i + 1”更改为“i = i + i”,结果不同。原来java真的很擅长“+1”
  • 您的机器规格是什么?我没有在这台机器上安装 Java,很难相信我的机器会比你的机器快得多(AMD Phenom II X6 1090T 3.2 Ghz,16 Gb RAM,Win 10 Pro)。我只让 for 循环像上面显示的 Paul Harkin 一样成为一个普通的空循环,并得到 1.06 - 1.07 秒(有 1000 个 goroutine)。
【解决方案2】:

i = i+i 测试很傻,因为它几乎只执行了 24 次加法和 24 次

与 Thread 相比,Executors.newFixedThreadPool 更接近 Java 等价的 goroutine,在热身的情况下尝试,你可能会得到不同的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-29
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多