返回信息流真相:go的默认int类型是和平台相关的,至少32位,但在x86_64的linux下是64位。所以每次go都在做64位的除法运算,而java在做32位的除法运算。真相见着里: https://bbs.byr.cn/#!article/Golang/253 ,换成同样的位数,go和java仍然有差别,但远远没有2倍那么多。
程序很简单:判断一个数是不是质数。
Java程序:
package cn.byr.nuanyangyang.prime;
public class IsPrime {
private static int NUM = 111181111;
private static boolean isPrime(int n) {
int i;
for (i = 2; i < n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
long t1 = System.nanoTime();
boolean result = isPrime(NUM);
long t2 = System.nanoTime();
System.out.printf("Result: %s, time: %f seconds", result, (double) (t2 - t1) / 1000000000.0);
}
}
结果:
Result: true, time: 0.523433 seconds
Go程序:
package main
import (
"fmt"
"time"
)
func isPrime(n int) bool {
for i := 2; i < n; i++ {
if n%i == 0 {
return false
}
}
return true
}
const NUM = 111181111
func main() {
t1 := time.Now() // 不是单调的。应该使用Linux的MONOTONIC_CLOCK
result := isPrime(NUM)
t2 := time.Now()
fmt.Printf("Result: %v, time: %v seconds", result, t2.Sub(t1).Seconds())
}
结果:
Result: true, time: 1.1724201 seconds
可是Go不是预编译成机器码的吗?怎么会连跑在虚拟机上的Java都拼不过?
环境:intel core i5-2450 2.5ghz 双核,Windows 10, oracle jdk 1.8,go 1.6.2
Go程序用go install ./...直接编译,默认是开优化的。
这是一条镜像帖。来源:北邮人论坛 / golang / #177同步于 2016/6/8
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Golang机器人发帖
【继续黑Go语言】这段程序Java速度是Go的2倍?
nuanyangyang
2016/6/8镜像同步30 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
公平起见,再贴一个C++的:
#include <cstdio>
#include <chrono>
using namespace std;
using namespace std::chrono;
int NUM = 111181111;
bool is_prime(int n) {
int i;
for(i = 2; i < n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
int main() {
bool result;
time_point<steady_clock> t1, t2;
t1 = steady_clock::now();
result = is_prime(NUM);
t2 = steady_clock::now();
printf("Result: %s, time: %lf seconds\n", result?"true":"false",
duration<double>(t2-t1).count());
return 0;
}
运行结果:
isprime-O3
Result: true, time: 0.521027 seconds
isprime-O0
Result: true, time: 0.523990 seconds
G++版本:g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 5.3.0
为了不服气专门去搜了下,还是有成效的:http://benchmarksgame.alioth.debian.org/u64q/go.html
reverse-complement
source secs KB gz cpu cpu load
Go
0.53 88,368 1278 0.94 28% 47% 77% 31%
Java
1.17 345,940 1661 2.42 41% 43% 57% 72%
fasta-redux
source secs KB gz cpu cpu load
Go
1.91 1,860 1236 1.90 1% 1% 100% 1%
Java
2.53 31,648 1443 2.59 3% 98% 1% 3%
mandelbrot
source secs KB gz cpu cpu load
Go
6.44 32,044 894 25.58 99% 100% 100% 100%
Java
7.14 88,236 796 27.93 97% 98% 98% 99%
pidigits
source secs KB gz cpu cpu load
Go
2.85 11,140 685 2.88 1% 35% 35% 32%
Java
3.11 33,364 938 3.20 99% 2% 2% 2%
spectral-norm
source secs KB gz cpu cpu load
Go
3.95 2,724 536 15.70 99% 99% 99% 100%
Java
4.26 32,244 950 16.41 96% 98% 95% 97%
n-body
source secs KB gz cpu cpu load
Go
22.00 1,948 1310 22.01 0% 1% 0% 100%
Java
22.53 28,212 1430 22.54 100% 7% 1% 1%
fasta
source secs KB gz cpu cpu load
Go
2.42 3,716 1344 6.32 78% 51% 69% 64%
Java
2.20 37,072 2457 6.37 81% 68% 72% 72%
fannkuch-redux
source secs KB gz cpu cpu load
Go
16.41 1,884 900 65.25 99% 100% 99% 100%
Java
13.65 31,996 1282 53.64 99% 98% 99% 98%
regex-dna
source secs KB gz cpu cpu load
Go
16.85 660,712 789 48.83 99% 64% 63% 64%
Java
8.23 753,008 929 24.56 71% 78% 81% 70%
k-nucleotide
source secs KB gz cpu cpu load
Go
42.59 266,232 1531 144.56 95% 68% 96% 82%
Java
12.87 462,488 1602 44.44 98% 81% 85% 82%
binary-trees
source secs KB gz cpu cpu load
Go
47.71 322,256 694 185.02 98% 97% 97% 97%
Java
11.51 622,328 889 40.10 86% 86% 92% 87%
Go
go version go1.6 linux/amd64
Java
java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)
我测试了下golang版本和c++,因为不会Java就没有对Java进行测试.
测试工具: Linux下perf工具测试:
测试命令: ./perf stat <YourProgram>
测试环境: Linux, 内核2.3.32,cpp编译器 g++ 4.8.2, golang版本1.5.1
测试结果:
CPP:
Result: true, time: 0.479323 seconds
Performance counter stats for './CppIsPrime':
479.921555 task-clock # 0.998 CPUs utilized
4 context-switches # 0.000 M/sec
5 CPU-migrations # 0.000 M/sec
265 page-faults # 0.001 M/sec
1,006,850,778 cycles # 2.098 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
894,211,888 instructions # 0.89 insns per cycle
223,264,768 branches # 465.211 M/sec
18,146 branch-misses # 0.01% of all branches
0.480895384 seconds time elapsed
Golang:
Result: true, time: 1.703589348 seconds
Performance counter stats for './IsPrime':
1703.209272 task-clock # 0.999 CPUs utilized
243 context-switches # 0.000 M/sec
22 CPU-migrations # 0.000 M/sec
306 page-faults # 0.000 M/sec
3,572,731,361 cycles # 2.098 GHz
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
1,231,209,057 instructions # 0.34 insns per cycle
335,199,164 branches # 196.804 M/sec
19,420 branch-misses # 0.01% of all branches
1.704948443 seconds time elapsed
测试结果分析:
从perf输出可以看出,golang比较慢的原因有两个
1. g++ 生成的汇编指令更短
894,211,888 instructions(cpp) VS 1,231,209,057 instructions(golang)
2. g++指令使用更加高效(主要原因):
# 0.89 insns per cycle(cpp) VS # 0.34 insns per cycle
cpp 每个时钟周期执行0.89个指令,golang 每个时钟周期执行0.34个指令。
还有一个很小的原因,反汇编golang和cpp生成的二进展文件,golang做了一个检查(CMPQ $-0x1, CX,汇编代码没全看明白),g++没有这个指令.
【 在 gaotianlong 的大作中提到: 】
: 我测试了下golang版本和c++,因为不会Java就没有对Java进行测试.
: 测试工具: Linux下perf工具测试:
: 测试命令: ./perf stat <YourProgram>
: ...................
干得漂亮。
我想不仅仅是个CMPQ吧,紧接着应该是一个Jxx指令,比如JE,JNE什么的。
这应该是java的JIT的功劳吧?我猜是因为java监视到了这段代码很hot,然后就直接把它生成机器码去执行了。
现在go刚出来,各种优化还比不上老牌的java,但我觉得go是有超过java的潜质的。
【 在 ykprocess 的大作中提到: 】
: 这应该是java的JIT的功劳吧?我猜是因为java监视到了这段代码很hot,然后就直接把它生成机器码去执行了。
嗯。Java的JIT可以编译出堪比C的高效程序。
: 现在go刚出来,各种优化还比不上老牌的java,但我觉得go是有超过java的潜质的。
Go其实已经出来好久了。但是,究竟做多少优化,就是编译器开发人员的决策了。我听说(只是听说而已,没有实证)Go语言故意不做太多的优化,以平衡编译速度和运行速度。虽然我并不完全赞同这个决策,但这大概可以说明Go的现状。编译器做多少优化是编译器的实现细节。Go是一个语言。如果有更好的决策,达到和C++/Java一样快是完全可能的。