摘要:代码在本文最后,首先是,编译出字节码耗时约,运行字节码耗时约,。也有解释过程,字节码需要由虚拟机解释执行。而引擎的做法是更接近二哥的,在编译阶段的过程是源码抽象语法树字节码中间代码。于是大量的字节码优化措施被延后,比如。
简单性能测试
首先,我们先来做一个简单的性能测试,对比一下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)的时候,说我这个例子还不错吧。
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 参考
《你所不知道的JavaScript(上卷)》
《WebKit技术内幕》
《深入浅出Node.js》
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79492.html
摘要:性能测试工具操作测试代码测试结果黄条代表每秒可执行测试函数的次数,当然越多越快。务必减少操作,减少无意义的路径的查找。其他测试测试代码这些测试代码来自,官网的例子这是一个空的非循环测试。正则表达式测试,数组的,新建日期,稍微慢了些。 性能测试工具 JSLitmus dom操作 测试代码 ...
摘要:本章将会深入谷歌引擎的内部结构。一个引擎可以用标准解释程序或者即时编译器来实现,即时编译器即以某种形式把解释为字节码。引擎的由来引擎是由谷歌开源并以语言编写。注意到没有使用中间字节码来表示,这样就不需要解释器了。 原文请查阅这里,略有删减。 本系列持续更新中,Github 地址请查阅这里。 这是 JavaScript 工作原理的第二章。 本章将会深入谷歌 V8 引擎的内部结构。我们也会...
摘要:引擎可以用标准解释器或即时编译器来实现,即时编译器以某种形式将代码编译为字节码。这里的主要区别在于不生成字节码或任何中间代码。请注意,不使用中间字节码表示法,不需要解释器。这允许在正常执行期间非常短的暂停。 本系列的第一篇文章重点介绍了引擎,运行时和调用栈的概述。第二篇文章将深入V8的JavaScript引擎的内部。我们还会提供一些关于如何编写更好的JavaScript代码的技巧。 概...
摘要:本文将会深入分析的引擎的内部实现。该引擎使用在谷歌浏览器内部。同其他现代引擎如或所做的一样,通过实现即时编译器在执行时将代码编译成机器代码。这可使正常执行期间只发生相当短的暂停。 原文 How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code 几周前我们开始了一个系列博文旨在深入...
摘要:第二篇文章将深入谷歌的引擎的内部。引擎可以实现为标准解释器,或者以某种形式将编译为字节码的即时编译器。这个引擎是在谷歌中使用的,但是,与其他引擎不同的是也用于流行的。一种更复杂的优化编译器,生成高度优化的代码。不是唯一能够做到的引擎。 本系列的 第一篇文章 主要介绍引擎、运行时和调用堆栈。第二篇文章将深入谷歌 V8 的JavaScript引擎的内部。 想阅读更多优质文章请猛戳GitHu...
阅读 1857·2021-09-22 15:45
阅读 1638·2019-08-30 15:55
阅读 1828·2019-08-29 11:16
阅读 3301·2019-08-26 11:44
阅读 701·2019-08-23 17:58
阅读 2697·2019-08-23 12:25
阅读 1623·2019-08-22 17:15
阅读 3596·2019-08-22 16:09