简朴机能测试
起首,我们先来做一个简朴的机能测试,对照一下Java,JavaScript,PHP,Ruby这四门言语。这个机能测试,是盘算斐波那契数列(兔子数列)。比方盘算n=5的兔子数列,效果是:1,1,2,3,5,8,13,21,34,55(1+1=2…21+34=35)。
这很轻易经由历程一个递返来完成,JavaScript代码以下:
function fs(n) {
if (n <= 2) {
return 1;
} else {
return fs(n - 1) + fs(n - 2);
}
}
可以看出,这个测试主要着重CPU栈操纵。
以上面这个函数为基本,加上一些逻辑,离别运用Java,JavaScript,PHP,Ruby这四门言语编写了剧本,盘算n=40的兔子数列,我们看一下效果吧。(代码在本文末了,^_^)
起首是Java,编译出字节码耗时约1s,运转字节码耗时约1s,666。
其次是JavaScript,在node环境下运转耗时约3.5s,在浏览器环境(Safari)下约8s,66。
接着是Ruby,出人意表的效果,约39s,6不起来了。
末了是PHP,约80s,233。
C或许C++的代码我没有写,一定跑得比狗还快。
这个简朴机能测试并不能申明言语好坏,只是比较好玩罢了,代码在本文末了,有兴致可以去运转一下。
Java佼佼不群的缘由
静态范例vs动态范例
静态范例言语指的是编译的时刻就可以晓得每一个变量的范例,我们编程的时刻固然也须要给定范例,如Java中的整型int,浮点型float等。
动态范例言语指的是运转的时刻才可以晓得每一个变量的范例,编程的时刻也无需显现指定范例,如JavaScript中的var,PHP中的$。
看上去,静态范例照样动态范例对机能没什么影响,实际上却影响很大。
归纳综合来讲就是,静态范例言语在编译后会大批应用范例已知的上风,比方int范例,占用4个字节,编译后的代码就可以运用内存地点加偏移量的要领存取变量。而地点+偏移量的算法汇编异常轻易完成。
那动态范例言语是怎样做的呢?归纳综合的来讲就是当作字符串统统存下来,今后存取就用字符串婚配。
可以感受到这儿存在的机能差别了吗?
编译型vs诠释性
编译型言语,就像C/C++,代码要经由编译器编译成可实行顺序后才可以运转。这个编译历程没什么时候请求,所以编译器可以做大批代码优化步伐,有时刻编译要良久良久。
诠释型言语,就像JavaScript,就是引擎直接读源码,然后就出效果,固然这模样做效力异常低。就像靠人脑去读源码,然后写答案一样。
奇葩型言语,就像Java,有编译历程,但编译产出的是中间代码(字节码),这个历程也有充足的时候做优化。也有诠释历程,字节码须要由Java虚拟机诠释实行。
从这儿,也许可以明白,为何C/C++运转效力比Java更高。由于不管怎么说,直接运转二进制码都比诠释实行字节码来得快吧。
所以,风趣的事变就来了,C/C++是老大,Java是二哥,一群诠释型剧本言语是小弟们。老大,独孤求败。二哥,想法子和老大站在一条线上。小弟们,尽全力跟上二哥。
当代JavaScript引擎的勤奋
先来看看,Java虚拟机做了哪些勤奋?
Java想的一定是优化虚拟机诠释实行字节码的速率,这儿恰是和老大拉开差异的处所。从老大那学了许多招。个中主要的一招就是JIT(Just-In-Time),主要的头脑就是诠释器在诠释字节码的时刻,会将部份字节码转化成当地代码(汇编代码),如许可以被CPU直接实行,而不是诠释实行,从而极大地进步机能。
重点来看看,JavaScript引擎做了哪些勤奋?
JavaScript从先辈那边进修了许多,总结来讲有:
优化数据示意,填补动态范例的机能缺点
引入一个编译历程,而不是直接诠释实行,但这个编译历程和运转是一同的,时候的衡量变得异常主要。
JIT手艺,与Java中的JIT道理雷同
V8引擎与JavaScriptCore引擎
各个JavaScript优化的详细完成不太一样。
举例子来讲,V8引擎关于编译和JIT的做法是,在编译阶段的历程是:源码=》笼统语法树=》当地代码。个中从笼统语法树到当地代码的历程运用的是JIT全码生成器,其作用是将笼统语法树转换成各个硬件平台和直接运转的当地代码。V8引擎的这类思绪看起来像想要超出二哥Java,直接学老大C/C++啊。
而JavaScriptCore引擎的做法是更靠近二哥的,在编译阶段的历程是:源码=》笼统语法树=》字节码(中间代码)。对这个阶段像极了二哥Java的编译历程,只是这里小弟可没有富余的时候做优化。因而大批的字节码优化步伐被延后,比方JIT。JavaScriptCore引擎运用DFG JIT、LLVM等继承对字节码做优化。
衡量时候很主要,一个很好的优化步伐但耗时太多,引入今后反而让JavaScript团体的运转时候变长了,得不偿失。别的,另有许多人提出,要不要完整抄二哥的,就是也引入一个提早编译的历程,233
Ruby、PHP为何在前面的测试中落败
详细缘由能够照样在引擎吧,能够它们的引擎远没有像V8这么勤奋。
总结
起首,关于底层的明白,有助于编写上层的代码。比方如今我们去明白JavaScript代码的时刻,会更深入。详细可以看这篇文章尝尝,《经由历程这一段代码,让我们重新认识JavaScript》。
其次,多一些话题吧,比方今后和火伴谈起V8引擎(装B)的时刻,说我这个例子还不错吧。
机能测试代码,Java、JavaScript、Ruby、PHP盘算斐波那契数列(兔子数列)
Java
import java.util.Date;
public class Fbnq {
public static void main(String []args) {
int num = 40;
long startTime = new Date().getTime();
//System.out.println(startTime);
String result = fslog(num);
long endTime = new Date().getTime();
//System.out.println(endTime);
float needTme = (endTime - startTime)/1000;
System.out.println("time:"+needTme+"s,result:"+result);
}
public static int fs (int n){
if(n <= 2){
return 1;
}else{
return fs(n-1)+fs(n-2);
}
}
public static String fslog(int num){
String rsString = "";
for(int i=1;i<=num;i++){
int rs = fs(i);
System.out.println(rs);
if(i == 1){
rsString = rsString + rs;
}else{
rsString = rsString + "," + rs;
}
}
return rsString;
}
}
JavaScript
var num = 40;
var startDate = new Date().getTime();
var result = logfs(num);
var endDate = new Date().getTime();
console.log("time:" + ((endDate - startDate) / 1000) + "s", "result:" + result);
function logfs(num) {
var rsString = "";
for (var i = 1; i <= num; i++) {
var rs = fs(i);
if (i === 1) {
rsString = rsString + rs;
} else {
rsString = rsString + "," + rs;
}
console.log(rs);
}
return rsString;
function fs(n) {
if (n <= 2) {
return 1;
} else {
return fs(n - 1) + fs(n - 2);
}
}
}
Ruby
def fs (n)
if n < 2
return 1;
else
return (fs (n-1)) + (fs (n-2));
end
end
def fslog (num)
num = num - 1;
rsString = "";
for i in 1..num
rs = fs i;
puts rs;
if i === 1
rsString = rsString + "#{rs}";
else
rsString = rsString + ",#{rs}";
end
end
return rsString;
end
num = 40;
startTime = Time.now.to_f*1000;
rsString = fslog num;
endTime = Time.now.to_f*1000;
needTime = (endTime - startTime)/1000;
puts "time:#{needTime}s,result:#{rsString}";
Php
<?php
$num = 40;
$startTime = microtime(true)*1000;
var_dump($startTime);
consoleLog($startTime);
$result = fslog($num);
$endTime = microtime(true)*1000;
consoleLog("time:".(($endTime - $startTime)/1000)."s,result:".$result);
function fs($n){
if ($n <= 2) {
return 1;
} else {
return fs($n - 1) + fs($n - 2);
}
}
function fslog($num){
$rsString = "";
for($i=1;$i<=$num;$i++){
$rs = fs($i);
if($i===1){
$rsString = $rsString."".$rs;
}else{
$rsString = $rsString.",".$rs;
}
consoleLog($rs);
}
return $rsString;
}
function consoleLog($str){
echo $str."\n";
}
?>
参考
《你所不晓得的JavaScript(上卷)》
《WebKit手艺内情》
《深入浅出Node.js》