在我的应用程序中,我正在生成遵循CPS样式的JavaScript代码。我'不'使用任何'延续'本身。没有异步行为,没有暂停和恢复,也没有回调。
只是代码遵循continuation passing style编程。
功能有许多阶段,每个阶段都进行处理并将结果传递给它的继续。
我发现CPS样式代码的性能非常差。以直接样式编写的代码几乎比CPS样式代码快150倍。
请检查以下代码。
以下代码均等同于
var res = data.store.bookshelf.book.author;
直接样式代码:
var data = { store : { bookshelf : {book : {author:"Douglas Crockford"}}}};
var t1 = new Date().getTime();
for(var i = 0; i < 1000*1000*100; i+=1){
var temp0 = data;
var temp1 = temp0.store;
var temp2 = temp1.bookshelf;
var temp3 = temp2.book;
var temp4 = temp3.author;
var res = temp4;
}
var t2 = new Date().getTime();
console.log(t2-t1);
上面的代码差不多95毫秒运行。
CPS样式代码:
var data = { store : { bookshelf : {book : {author:"Douglas Crockford"}}}};
// return the variable to the continuation
function cps_VARREF(x,f){
return f(x);
}
// get the value of the property from the variable and pass it to the continuation
function cps_CHILD(x,child,f){
return f(x[child]);
}
// simply return the input value, essentially closing the continuation chain
function ret_(x){
return x;
}
var t1 = new Date().getTime();
for(var i = 0; i < 1000*1000*100; i+=1){
var res = function(c_){
return cps_VARREF(data,function(x1){
return cps_CHILD(x1,"store",function(x2){
return cps_CHILD(x2,"bookshelf",function(x3){
return cps_CHILD(x3,"book",function(x4){
return cps_CHILD(x4,"author",c_);});});});});}(ret_);
}
var t2 = new Date().getTime();
console.log(t2-t1);
以上CPS样式代码运行时间为15000毫秒
我有什么办法可以改善CPS风格的代码吗?或者JavaScript本身不适合CPS样式代码?
以上测试在node.js版本0.6.12上完成
有人可以就这个问题说清楚吗?
谢谢,
答案 0 :(得分:1)
至少有两个可能导致剧烈放缓的原因。首先,您要使用动态查找替换“本机”属性查找。
V8可以随时优化对象,因此访问属性的速度更快。它不是使用哈希表来按名称查找属性,而是跟踪内部“类”,以便它可以从已知地址查找属性。所以data.store
只是一个快速指针比较,以确保对象是期望的类型和索引指针加载。
在cps_CHILD
函数中,它无法进行优化,因为它不知道将提前访问哪个属性(每次调用它时它都会更改)。动态查找强制V8回退到哈希表查找,这比优化的静态查找慢。
另一个问题是函数调用的开销。每次将嵌套函数传递给下一个函数时,都必须重新创建每个嵌套函数。它们不应该每次都需要编译,但它仍然需要在新的上下文中创建。
答案 1 :(得分:0)
你应该知道在运行时解析一些东西,比如你在循环中输入的那些匿名函数。在每个新的“i”上,将再次创建新的“构造函数”,并为每个anonymus函数创建其原型。这就是它变慢的原因。这是新的测试。尝试为每个匿名函数定义实际函数,将它们嵌套在循环外,它应该提高程序的性能。
这是代码,它在你的CPS样式代码中很深入,但只定义了一次函数
var data = { store : { bookshelf : {book : {author:"Douglas Crockford"}}}};
function getValueFrom(data){
return data;
}
function getAuthorFrom(data){
return getValueFrom(data);
}
function getBookFrom(data){
return getAuthorFrom(data["book"]);
}
function getBookShelfFrom(data){
return getBookFrom(data["bookshelf"]);
}
function getStoreFrom(data){
return getBookShelfFrom(data["store"]);
}
function getAuthor(data){
return getAuthor(data);
}
var t1 = new Date().getTime();
for(var i = 0; i < 1000*1000*100; i+=1){
var res = getStoreFrom(data);
}
var t2 = new Date().getTime();
console.log(t2-t1);
它的运行速度提高了4-5倍。由于JS引擎需要寻找函数原型,所以它仍然较慢,所以它可以放在堆上执行。在第一种情况下(非CSP样式)当您使用点访问属性时,JS引擎仅按键(JS对象属性)查询哈希值以获取其值。它工作得更快,因为它只需要处理内存引用。