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