Ash*_*kes 20 javascript recursion performance
前几天我注意到了一个问题(减少Javascript CPU使用率),我很感兴趣.
基本上这个人想要逐个字符地加密一些文件.显然,一次性完成这一切将锁定浏览器.
他的第一个想法是在块中一次大约1kb的字符串,然后暂停X ms,这样它将允许用户在处理之间保持与页面的交互.他还考虑过使用webWorkers(最好的主意),但显然不是跨浏览器.
现在我真的不想进入为什么这在javascript中可能不是一个好主意.但我想知道我是否能想出一个解决方案.
我记得道格拉斯克罗克福德在js conf观看了一段视频.该视频与node.js和事件循环有关.但我记得他在谈论将长时间运行的函数分解为单个块,因此新调用的函数将进入事件循环的末尾.而不是通过长时间运行的任务阻塞事件循环,防止其他任何事情发生.
我知道这是一个值得我调查的解决方案.作为一名前端开发人员,我从未真正体验过JS中长时间运行的任务,并且热衷于了解如何分解它们以及它们的运行方式.
我决定尝试一个递归函数out,它从0ms的setTimeout内部调用自己.我认为这会在事件循环中为其他任何想要在运行时发生的事件提供中断.但我也认为,虽然没有其他任何事情你将得到最大的计算.
这就是我想出的.
(我要为代码道歉.我正在试验控制台,所以这很快而且很脏.)
function test(i, ar, callback, start){
if ( ar === undefined ){
var ar = [],
start = new Date;
};
if ( ar.length < i ){
ar.push( i - ( i - ar.length ) );
setTimeout(function(){
test( i, ar, callback, start);
},0);
}
else {
callback(ar, start);
};
}
Run Code Online (Sandbox Code Playgroud)
(您可以将此代码粘贴到控制台中,它将起作用)
本质上,函数所做的是获取一个数字,创建一个数组并调用自身,同时array.length < number将计数推送到数组中.它将第一次调用中创建的数组传递给所有后续调用.
我测试了它,它似乎完全按照预期工作.只有它的表现相当差.我测试了它..
(再次这不是性感的代码)
test(5000, undefined, function(ar, start ){
var finish = new Date;
console.log(
ar.length,
'timeTaken: ', finish - start
);
});
Run Code Online (Sandbox Code Playgroud)
现在我显然想知道完成需要多长时间,上面的代码需要大约20秒.现在在我看来,JS不应该花20秒来计算5000.加上它正在进行一些计算和处理以将项目推送到数组中这一事实.但仍然20多岁有点陡峭.
所以我决定同时产生几个,看看它是如何影响浏览器性能和计算速度的.
(代码没有任何性感)
function foo(){
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 1' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 2' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 3' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 4' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 5' ) });
};
Run Code Online (Sandbox Code Playgroud)
总共五个,同时运行并且不会导致浏览器挂起.
在流程结束后,所有结果几乎完全同时返回.所有人都需要大约21.5秒来完成.这比它自己慢了1.5秒.但是我正在窗口上移动鼠标周围的元素,这些元素:hover只是为了确保浏览器仍在响应,所以这可能会导致1.5s的一些开销.
因此,当这些功能显然并行运行时,浏览器中会留下更多的计算量.
是否有人能够在这里解释性能方面的变化,并详细说明如何改进这样的功能?
只是疯了我做了这个..
function foo(){
var count = 100000000000000000000000000000000000000;
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 1' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 2' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 3' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 4' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 5' ) });
};
Run Code Online (Sandbox Code Playgroud)
它一直在运行我写这篇文章的所有时间,并且仍然在努力.浏览器没有抱怨或挂起.我会在结束时添加完成时间.
Ray*_*nos 14
setTimeout没有最小的延迟0ms.最小延迟在5ms-20ms范围内,取决于浏览器.
我自己的个人测试显示,setTimeout不会立即回到事件堆栈
它再次被调用之前有一个最小的时间延迟
var s = new Date(),
count = 10000,
cb = after(count, function() {
console.log(new Date() - s);
});
doo(count, function() {
test(10, undefined, cb);
});
Run Code Online (Sandbox Code Playgroud)
似乎每个人setTimeout都必须等待至少4毫秒再次被召唤.但那是瓶颈.个人延迟setTimeout.
如果您并行安排100个或更多这些,那么它将起作用.
我们如何优化这个?
var s = new Date(),
count = 100,
cb = after(count, function() {
console.log(new Date() - s);
}),
array = [];
doo(count, function() {
test(10, array, cb);
});
Run Code Online (Sandbox Code Playgroud)
设置100在同一阵列上并行运行.这将避免主要瓶颈,即setTimeout延迟.
以上完成时间为2ms.
var s = new Date(),
count = 1000,
cb = after(count, function() {
console.log(new Date() - s);
}),
array = [];
doo(count, function() {
test(1000, array, cb);
});
Run Code Online (Sandbox Code Playgroud)
在7毫秒内完成
var s = new Date(),
count = 1000,
cb = after(1, function() {
console.log(new Date() - s);
}),
array = [];
doo(count, function() {
test(1000000, array, cb);
});
Run Code Online (Sandbox Code Playgroud)
并行运行1000个工作大致是最佳的.但是你会开始遇到瓶颈.数到100万仍然需要4500ms.
您的问题是与工作单元相比的开销问题.你的setTimeout开销非常高,而你的工作单元ar.push非常低.该解决方案是一种称为块处理的旧优化技术.不是每次调用处理一个UoW,而是需要处理一块UoW."块"的大小取决于每个UoW花费的时间以及每个setTimeout/call/iteration(在UI变得无响应之前)可以花费的最长时间.
function test(i, ar, callback, start){
if ( ar === undefined ){
var ar = [],
start = new Date;
};
if ( ar.length < i ){
// **** process a block **** //
for(var x=0; x<50 && ar.length<i; x++){
ar.push( i - ( i - ar.length ) );
}
setTimeout(function(){
test( i, ar, callback, start);
},0);
}
else {
callback(ar, start);
};
}
Run Code Online (Sandbox Code Playgroud)
您必须处理最大的块,而不会导致用户出现UI /性能问题.前面的运行速度快〜50倍(块的大小).
这与我们使用缓冲区读取文件而不是一次读取一个字节的原因相同.
| 归档时间: |
|
| 查看次数: |
3514 次 |
| 最近记录: |