Java和JavaScript运算性能对比

代码都在同一个window操作系统的机器上运行,分别取Eclipse中运行的Java、谷歌浏览器中运行的JavaScript、Nodejs中运行的JavaScript的结果进行对比。
其中,Jdk版本为1.8.0_181(64位),JavaScript运行环境为Google Chrome72.0.3626.119(64位),Nodejs版本为10.4.1(64位)。

超大规模运算(万亿级)

Java代码

public static void calc1(){
		long value=0;
		long start = System.currentTimeMillis();
		for (int i = 0; i < 1000; i++) {
			for (int j = 0; j < 1000; j++) {
				for (int k = 0; k < 1000; k++) {
					for (int k2 = 0; k2 < 10; k2++) {
						value+=k2*k+j+i;
					}
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println(value);
		System.out.println(end - start);
}

JavaScript代码

{
	function calc1(){
	    let value=0;
	    let start  = new Date().getTime();
	    for (let i = 0; i < 1000; i++) {
	        for (let j = 0; j < 1000; j++) {
	            for (let k = 0; k < 1000; k++) {
	                for (let k2 = 0; k2 < 10; k2++) {
	                    value+=k2*k+j+i;
	                }
	            }
	        }
	    }
	    let end = new Date().getTime();
	    console.log(value);
	    console.log(end - start);
	}
}

运行结果

考虑到寄存器缓存对性能的影响,每个方法都执行6次并去除第一次运行时间后取平均值。Java输出5711ms;JavaScript浏览器输出14705ms;Nodejs环境输出14492ms
第一次运行时间对比,Java输出8230ms;Js浏览器输出19110ms;Nodejs环境输出17387ms。

小规模运算(百万级以内)

Java代码

public static void calc2(){
		double value=0;
		double temp1=0;
		double temp2=0;
		double temp3=0;
		double temp4=0;
		double temp5=0;
		long start = System.nanoTime();
		double j=0;
		for (double i = 0; i < 1000000; i++,j--) {
			temp1+=i*j;
			temp2+=i;
			temp3+=j;
			temp4+=i*i;
			temp5+=j*j;
		}
		value=(temp1-temp2*temp3/1000000)/Math.sqrt((temp4-temp2*temp2/1000000)*(temp5-temp3*temp3/1000000));
		long end = System.nanoTime();
		System.out.println(value);
		System.out.println(end - start);
}

在测试中,特意做了这样一个结果记录:如果Java程序在运算时出现类型转换,运算效率将会大幅下降,如下calc3()方法和calc2()几乎相同,除了将for循环中的i和j的声明类型换成int,这将引起整数向浮点数的运行时转换:

public static void calc3(){
		//运行时类型转换
		double value=0;
		double temp1=0;
		double temp2=0;
		double temp3=0;
		double temp4=0;
		double temp5=0;
		long start = System.nanoTime();
		int j=0;
		for (int i = 0; i < 1000000; i++,j--) {
			temp1+=i*j;
			temp2+=i;
			temp3+=j;
			temp4+=i*i;
			temp5+=j*j;
		}
		value=(temp1-temp2*temp3/1000000)/Math.sqrt((temp4-temp2*temp2/1000000)*(temp5-temp3*temp3/1000000));
		long end = System.nanoTime();
		System.out.println(value);
		System.out.println(end - start);
}

JavaScript代码

{
	function calc2(){
	    let  value= 0,temp1= 0,temp2= 0,temp3= 0,temp4= 0,temp5= 0,j=0;
	    let  start = new Date().getTime();
	    for (let i = 0; i < 1000000; i++,j--) {
	        temp1+=i*j;
	        temp2+=i;
	        temp3+=j;
	        temp4+=i*i;
	        temp5+=j*j;
	    }
	    value=(temp1-temp2*temp3/1000000)/Math.sqrt((temp4-temp2*temp2/1000000)*(temp5-temp3*temp3/1000000));
	    let end = new Date().getTime();
	    console.log(value);
	    console.log(end - start);
	}
}

运行结果

考虑到寄存器缓存对性能的影响,每个方法都执行6次并去除第一次运行时间后取平均值。Java中calc2()输出2.52ms,calc3()输出3.7ms;JavaScript浏览器输出2.6ms;Nodejs环境输出2.4ms。
第一次运行时间对比,Java中calc2()输出6.8ms,calc3()输出8.6ms;JavaScript浏览器输出14ms;Nodejs环境输出13ms。

结论

运算级别

超大规模运算(万亿级)

小规模运算(百万级以内)

第一次运行时间

平均运行时间

第一次运行时间

平均运行时间

Java

8230ms

5711ms

6.8ms

2.52ms

Java中的calc3()

-

-

8.6ms

3.7ms

JavaScript浏览器

19110ms

14705ms

14ms

2.6ms

Nodejs

17387ms

14492ms

13ms

2.4ms

观察发现:
1.在超大规模计算时,Java运行时间明显低于JavaScript;
2.在小规模运算时,Java运行时间和JavaScript运行时间相差不大;
3.Nodejs的运行时间略快于浏览器环境;
4.不论Java或者Javascript,多次执行的代码优化都是非常明显的;
5.Java作为强类型语言,在编程时要确保不出现运行时类型转换,否则性能将大大降低;
6.从程序输出发现,Java每次执行时间跳动较大(有可能已经是第四次执行了,执行时间却接近第一次执行时间)。而Nodejs每次执行时间是最稳定的,除了第一次执行时间较长,之后每次执行时间几乎不变;

以下是对于出现这样结果的一些分析:
1.浏览器环境下,页面程序需要处理的问题会更多,例如Javascript代码是否会引起HTML修改,这部分检查导致了浏览器环境下的代码效率注定不如纯后台的运算环境;
2.JavaScript是弱类型语言,因此在运算数据上升时,程序会在内部执行从小值域数据类型到大值域数据类型的转换,这是一个很大的性能消耗,这个转换可以从“超大规模运算”的对比结果中看出来(JavaScript在执行超大规模运算时速度明显很慢);
3.同样的,如果Java程序中由于编程粗心,引入了运行时类型转换(例如整数转浮点数,本文中的calc3()函数),那对应的Java程序的性能将会大幅下降。