retweet

由mongodb引发的Javascript Eval 性能测试

本文是半成品, 已经过时; 请参考另一篇文章: mongodb的javascript性能
h3. Javascript Eval Scope 在很多场合, Javascript的代码片段常常需要传递到另一个系统运行. 比如在解析js模板时, 常常将模板字符替换成js代码, 然后调用eval执行它们. 现在, 后端程序也常常遇到这种情况. 最近我遭遇了mongodb的一个问题, 现在问题还没解决, 但涉及到这块, 需要先研究一下. h3. mongodb的db.eval mongodb使用javascript做shell, mongodb的db.eval可以提供给数据驱动与这种javascript shell类似的js接口. 这算是一种移动代码风格(Mobile Code Sytles)的架构设计吧. 我期望可以利用这个接口, 将一些处理逻辑放在mongodb的节点上运行. 避免在PHP里面多次获取和操作mongodb的数据. (这个需求类似传统数据库的存储过程) 但是测试却发现, 在mongodb里面跑javascript性能不理想. 我写了一段测试代码, 跑10000次循环:
function mytest(params) { 
	var str = "xxxxxxxxxxxxxxxxx0000000000";
	var data = str + str + str + str;
	var data = data + data + data + data;
	var max = 10000;
	var arr = [];

	for(var i=0; i<max; i++) {
		arr.push( data + " . " + data);
	}
	params.length = arr.length;
	return params;
}
结果, 居然浪费58毫秒. 要知道PHP里面才用了8毫秒. 太不可思议了. 我排除了db.eval的nolock因素, 以及使用store js , 以及直接在javascript shell里面运行, 结果都一样. 于是我猜测:是不是js执行引擎的问题? 我知道mongodb的javascript引擎是SpiderMonkey, 于是在firefox和chrome上对比测试. 结果浏览器运行速度惊人, firefox运行2毫秒, chrome运行0毫秒. 后来用nodejs运行, 结果1毫秒. 分析上面的这些测试结果, 感觉SpiderMonkey不会比V8慢这么多(firefox和chrome顶多差一倍). 难道是mongodb使用js引擎时, 初始化Context开销大? 这个有可能. 仔细看mongodb的源码, 发现db.eval的相关部分代码. 流程是: 是先为客户端分配一个Scope, (有个Scope Pool来做缓存) , 客户端代码转换成一个function, 然后执行这个函数. h3. 初步分析和测试 这里有几个问题: * Scope Pool是不是够用, 能否配置? * 客户端代码(String)转换成函数, 再执行的效率如何? 问题1先放下不谈. 还需要后续研究. 出于对第二个问题的优先兴趣, 我写了一段代码测试chrome和firefox:
function mytest(params) { 
	var str = "xxxxxxxxxxxxxxxxx0000000000";
	var data = str + str + str + str;
	var data = data + data + data + data;
	var max = 10000;
	var arr = [];

	for(var i=0; i<max; i++) {
		arr.push( data + " . " + data);
	}
	params.length = arr.length;
	return params;
}

var fun_str = mytest.toString();

function do_test() {
	var a=new Date();
	var max = 1000;
	for(var i=0; i<max; i++) {
		eval("var fun = " + fun_str + ";");
		fun({"type":"test"});
	}
	var b=new Date(); 
	console.log(b.valueOf()-a.valueOf());
}

function begin_test() {
	console.log(do_test());
}
在chrome上测试, 约1200毫秒; 在firefox上测试, 大约2100毫秒. 看起来符合上面V8比SpiderMonkey快一倍的观测. 但是代码内消耗资源的, 除了10000X1000次arr.push之外, 还有1000次eval操作也是大头. 于是我将eval次数改成10000, arr.push内循环次数改成1000, 进行比较测试. 结果发现chrome运行了5306毫秒, firefox运行了7014毫秒. 两者相差没那么大了. 看起来普通代码V8比SpiderMonkey快一倍, 但eval这种代码两者相差没那么大. h3. 使用nodejs和SpiderMonkey的js shell测试eval 为了专门测试eval, 我改变了测试方式, 使用nodejs和SpiderMonkey的js shell来执行测试.
function mytest(params) { 
	if(params.skip = 1) return params;

	var str = "xxxxxxxxxxxxxxxxx0000000000";
	var data = str + str + str + str;
	var data = data + data + data + data;
	var max = 1000;
	var arr = [];

	for(var i=0; i<max; i++) {
		arr.push( data + " . " + data);
	}
	params.length = arr.length;
	return params;
}


var fun_str = mytest.toString();

var printfun = (typeof console == 'object' && typeof console.log == 'function') ? console.log : print;

function do_test() {
	var a=new Date();
	var max = 10000;
	for(var i=0; i<max; i++) {
		eval("var fun = " + fun_str + ";");
		fun({"skip":1});  //只测试eval, 不测试执行
	}
	var b=new Date(); 
	printfun(b.valueOf()-a.valueOf());
}

function begin_test() {
	do_test();
}

begin_test();
结果发现, nodejs平均执行1013毫秒, SpiderMonkey js shell平均执行83毫秒, SpiderMonkey完胜. h3. 后续 mongodb Scope Pool的细节先放在一边, 以后再研究. 现在起码知道在Scope执行的环节, V8的eval执行是慢很多的, 但普通循环V8比SpiderMonkey约快一倍. 这就很矛盾了, 不管怎么说, 接下来, 我要赶紧试试用V8编译mongodb. 在mongodb中实际进行对比测试, 成功后再见. 很遗憾, 这是一篇没有结论的文章. ---- Update : 今天又仔细看mongodb的源码, 发现客户端传递来的code不是通过js的eval函数进入的js执行环境. 而是调用C++ API: Scope::createFunction( const char * code ) 产生一个ScriptingFunction对象. 所以可能这种方式的性能和js eval没直接关系.

--EOF--

若无特别说明,本站文章均为原创,转载请保留链接,谢谢

本文地址: http://www.dulao5.com/javascript/2011/06/04/javascript-eval-scope-performance-testing.textile