为什么用 Golang 写的一些函数运行起来比用 Java 还要慢?

我用 Golang 和 Java 测试了几个简单的函数。令我惊讶的是,Java 有时比 Golang 更快(尤其是在递归函数和标准库中的某些函数,如 math/rand.Rand)。我想知道为什么。这是我用于测试的一些代码和结果。

Java代码:


public class Performance {


    public static double calPi(int pointCount) {

        int inCircleCount = 0;


        double x, y;

        double Pi;


        for (int i = 0; i < pointCount; i++) {

            x = Math.random();

            y = Math.random();

            if (x * x + y * y < 1) {

                inCircleCount++;

            }

        }


        Pi = (4.0 * inCircleCount) / pointCount;


        return Pi;

    }


    public static double cal(double a, double b, double c) {

        return a * b / (c + 1) + a;

    }


    public static long fibonacci(long c) {

        if (c < 2)

            return c;

        return fibonacci(c - 2) + fibonacci(c - 1);

    }


    public static void main(String[] args) {


        System.out.println("Test 1");


        long startTime = System.currentTimeMillis();


        double result = 0.0;


        for (double i = 0.0; i < 1000000000; i = i + 1) {

            result += i * i;

        }


        long endTime = System.currentTimeMillis();


        float duration = (float) (endTime - startTime) / 1000;


        System.out.println("Result: " + result);

        System.out.println("Duration: " + duration + " s");


        System.out.println("Test 2");


        startTime = System.currentTimeMillis();


        long resultInt = fibonacci(50);


        endTime = System.currentTimeMillis();


        duration = (float) (endTime - startTime) / 1000;


        System.out.println("Result: " + resultInt);

        System.out.println("Duration: " + duration + " s");


        System.out.println("Test 3");


        startTime = System.currentTimeMillis();


        result = 0.0;


        for (double i = 0; i < 100000000; i = i + 1) {

            result += Math.random();

        }


        endTime = System.currentTimeMillis();


        duration = (float) (endTime - startTime) / 1000;



    }

}


Test 2结果的差异真的让我震惊!请帮我找出原因,谢谢。如果有人可以给我示例来展示 Golang(与 Java)的优势,那就更好了。


慕姐4208626
浏览 135回答 1
1回答

哔哔one

Java 和 Golang 程序在执行之前都被编译成机器语言——这就是 JIT 代表 Java VM 的意思。至于性能比较,每个生成的机器码之间肯定有不那么细微的差异。不幸的是,我无法访问 Java JIT 编译器生成的机器码,但我们可以看一下 Go 编译器(v1.11.4-amd64)为函数生成的内容fibonacci:&nbsp; &nbsp; &nbsp; &nbsp; # Do the comparison&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; "c", AX&nbsp; &nbsp; &nbsp; &nbsp; CMPQ&nbsp; &nbsp; AX, $2&nbsp; &nbsp; &nbsp; &nbsp; JGE&nbsp; &nbsp; &nbsp;@ELSE&nbsp; &nbsp; &nbsp; &nbsp; # Save the func result&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; AX, "r"&nbsp; &nbsp; &nbsp; &nbsp; # Clean up and return&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; 24(SP), BP&nbsp; &nbsp; &nbsp; &nbsp; ADDQ&nbsp; &nbsp; $32, SP&nbsp; &nbsp; &nbsp; &nbsp; RET@ELSE:&nbsp; &nbsp; &nbsp; &nbsp; # Compute fib(c - 2)&nbsp; &nbsp; &nbsp; &nbsp; LEAQ&nbsp; &nbsp; -2(AX), CX&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; CX, (SP)&nbsp; &nbsp; &nbsp; &nbsp; CALL&nbsp; &nbsp; fibonacci&nbsp; &nbsp; &nbsp; &nbsp; # Save the call result&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; 8(SP), AX&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; AX, "temp"&nbsp; &nbsp; &nbsp; &nbsp; # Compute fib(c - 1)&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; "c", CX&nbsp; &nbsp; &nbsp; &nbsp; DECQ&nbsp; &nbsp; CX&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; CX, (SP)&nbsp; &nbsp; &nbsp; &nbsp; CALL&nbsp; &nbsp; fibonacci&nbsp; &nbsp; &nbsp; &nbsp; # Add previous results together&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; 16(SP), AX&nbsp; &nbsp; &nbsp; &nbsp; ADDQ&nbsp; &nbsp; 8(SP), AX&nbsp; &nbsp; &nbsp; &nbsp; # Save the func result&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; AX, "r"&nbsp; &nbsp; &nbsp; &nbsp; # Clean up and return&nbsp; &nbsp; &nbsp; &nbsp; MOVQ&nbsp; &nbsp; 24(SP), BP&nbsp; &nbsp; &nbsp; &nbsp; ADDQ&nbsp; &nbsp; $32, SP&nbsp; &nbsp; &nbsp; &nbsp; RET请注意,这段代码不是完全相同的输出,但我对其进行了一些修改以使其更加清晰。引用的变量是堆栈位置。我的结论是,虽然 Go 编译器确实采用了一些优化技术来生成更高性能的代码(请参阅编译器优化),但它在分配 CPU 寄存器方面做得不是很好(与 C 编译器生成的相比),并且依赖于堆栈太多,尤其是对于返回值——我认为必须有一个可能与语言工作方式相关的原因(例如多个返回值)。更新 1只是为了比较,这是 GCC (amd64) 为相同功能生成的机器码:&nbsp; &nbsp; &nbsp; &nbsp; pushq %rbp&nbsp; &nbsp; &nbsp; &nbsp; movq&nbsp; %rsp, %rbp&nbsp; &nbsp; &nbsp; &nbsp; pushq %r14&nbsp; &nbsp; &nbsp; &nbsp; pushq %rbx&nbsp; &nbsp; &nbsp; &nbsp; # Do the comparison&nbsp; &nbsp; &nbsp; &nbsp; movq&nbsp; %rdi, %rbx&nbsp; &nbsp; &nbsp; &nbsp; cmpq&nbsp; $2, %rbx&nbsp; &nbsp; &nbsp; &nbsp; jge @ELSE&nbsp; &nbsp; &nbsp; &nbsp; # Save "c" in "r"&nbsp; &nbsp; &nbsp; &nbsp; movq&nbsp; %rbx, %rax&nbsp; &nbsp; &nbsp; &nbsp; jmp @RETURN@ELSE:&nbsp; &nbsp; &nbsp; &nbsp; # Compute fib(i - 2)&nbsp; &nbsp; &nbsp; &nbsp; leaq&nbsp; -2(%rbx), %rdi&nbsp; &nbsp; &nbsp; &nbsp; callq fibonacci&nbsp; &nbsp; &nbsp; &nbsp; # Compute fib(i - 1)&nbsp; &nbsp; &nbsp; &nbsp; movq&nbsp; %rax, %r14&nbsp; &nbsp; &nbsp; &nbsp; decq&nbsp; %rbx&nbsp; &nbsp; &nbsp; &nbsp; movq&nbsp; %rbx, %rdi&nbsp; &nbsp; &nbsp; &nbsp; callq fibonacci&nbsp; &nbsp; &nbsp; &nbsp; # Add previous results together&nbsp; &nbsp; &nbsp; &nbsp; addq&nbsp; %r14, %rax@RETURN:&nbsp; &nbsp; &nbsp; &nbsp; popq&nbsp; %rbx&nbsp; &nbsp; &nbsp; &nbsp; popq&nbsp; %r14&nbsp; &nbsp; &nbsp; &nbsp; popq&nbsp; %rbp&nbsp; &nbsp; &nbsp; &nbsp; retq更新 2话虽如此,我坚信在实际项目中,语言运行时(例如对象分配、垃圾收集、调用间接、动态加载、并发支持等)会对程序的整体性能产生更大的影响,而不是功能级别的微优化。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go