为什么需要脚本引擎这里不多做说明。javascript就是使用最广的解析执行脚本语言,它背后就是一个脚本引擎。
脚本引擎分为客户端的脚本引擎和服务端的脚本引擎,特别是服务端的脚本引擎,脚本引擎的性能直接影响了脚本解析执行的效率,影响到系统数据处理的能力,所以提高脚本引擎的解析性能是优化的一个重要环节。
影响脚本引擎性能主要由2个方面:
1. 脚本引擎的技术平台
2. 脚本引擎的设计理念
脚本引擎的技术平台
在同一设计理念的情况下,c/c++下的脚本引擎性能要比java/c#下的脚本引擎性能要高,这取决于技术的选型,需要在开发效率和执行效率做一个取舍。
脚本引擎的设计理念
设计理念非常重要,设计的合理的java/c#脚本引擎甚至比设计不合理的c/c++脚本引擎效率还要高,这部分有较大的优化空间。
脚本引擎的优化
虽然我没系统地学习编译原理,但是,这么多年的开发经验告诉我,预编译的重要性。预编译的作用是把脚本语言的语句文本解析成便于脚本引擎高效执行的内部语句对象。
所以,脚本引擎优化的方向是彻底的预编译,然后是缓存。
这里强调彻底的预编译,是因为我待优化的脚本引擎没有做到完全的预编译,有部分还是进行实时语句文本解析,这导致性能未达到最优。缓存的重要性这里不多做介绍,把预编译的结果缓存重复使用。
先简单介绍几个脚本语法
【赋值语句】执行表达式expression并把结果赋值到变量var。
例子:
var = expression
【条件语句】if关键字后面表达式expression为真时执行内部子语句,否则执行后面的else if或else语句;
elseif关键字后面表达式expression为真时执行内部子语句,否则执行后面的else if或else语句;
在if和else if的判断都为假时才执行else的内部子语句;
if后允许出现0个或多个else if和0个或1个else。
例子:
if expression
//子语句
elseif expression
//子语句
else
//子语句
end
【循环语句:while...】当表达式expression值为真时执行内部子语句。
例子:
while expression
//子语句
end
预编译
优化前的预编译
优化后的预编译:与优化前相比每个语句的表达式(Expression1, Expression2…),被解析成了表达式对象。这部优化很关键,原来是每执行一个语句对象的时候都重新解析字符串成表达式对象后调用,现在是直接就能调用表达式对象。
以下是用作测试的脚本
测试脚本的内容主要是,创建1000个对象,为1000个对象进行属性赋值,进行Total的累计等。
优化的结果
调用上述的测试脚本1000次
优化前的结果
测试序号 | 耗时 |
1 | 67.7721806秒 |
2 | 63.3531118秒 |
3 | 61.1675436秒 |
4 | 61.017888秒 |
5 | 61.0923334秒 |
6 | 60.9598041秒 |
平均 | 62.56047692秒 |
优化后的结果
测试序号 | 耗时 |
1 | 0.5564288秒 |
2 | 0.4946134秒 |
3 | 0.5057536秒 |
4 | 0.4925313秒 |
5 | 0.505112秒 |
6 | 0.4988381秒 |
平均 | 0.508879533秒 |
效率的提升了120多倍
总结
这个优化的结果比我预想的要高很多,这也说明了,设计的理念对脚本引擎性能的影响是巨大的。