JavaScript 性能长时间运行的任务 [英] JavaScript Performance Long Running Tasks

查看:36
本文介绍了JavaScript 性能长时间运行的任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

前几天我注意到这里有一个问题(减少 Javascript CPU 使用率),我很感兴趣.

I noticed a question on here the other day ( Reducing Javascript CPU Usage ) and I was intrigued.

本质上,这家伙想逐个字符地加密一些文件.显然,一次性完成所有这些操作会锁定浏览器.

Essentially the guy wanted to encrypt some files character by character. Obviously doing all this in one go is going to lock up the browser.

他的第一个想法是一次以大约 1kb 的字符串块为单位进行操作,然后暂停 X 毫秒,这样用户就可以在处理之间继续与页面进行交互.他也考虑过使用 webWorkers(最好的主意),但它显然不是跨浏览器.

His first idea was to do it in chunks roughly 1kb's worth of string at a time, then pause for X ms so it would allow the user to keep interacting with the page between processing. He also considered using webWorkers ( the best idea ), but it obviously isn't cross browser.

现在我真的不想深入探讨为什么这在 javascript 中可能不是一个好主意.但我想看看我是否能想出一个解决方案.

Now I don't really want to go into why this probably isn't a good idea in javascript. But I wanted to see if I could come up with a solution.

我记得在 js conf 上观看了 Douglas Crockford 的视频.该视频与 node.js 和事件循环有关.但我记得他谈到将长时间运行的函数分解成单独的块,所以新调用的函数会到达事件循环的末尾.而不是用长时间运行的任务阻塞事件循环,防止其他任何事情发生.

I remembered watching a video by Douglas Crockford at js conf. The video was related to node.js and the event loop. But I remembered him talking about breaking long running functions down into individual chunks, so the newly called function goes to the end of the event loop. Instead of clogging the event loop up with a long running task, preventing anything else from happening.

我知道这是一个值得我研究的解决方案.作为一名前端开发人员,我从未真正体验过在 JS 中运行时间非常长的任务,并且很想知道如何分解它们以及它们如何执行.

I knew this was a solution worthy of my investigation. As a front-end Developer I have never really experienced extremely long running tasks in JS and was keen to find out about how to break them up and how they perform.

我决定尝试一个递归函数,它从 0 毫秒的 setTimeout 内部调用自己.我认为这将为事件循环中的任何其他想要在运行时发生的事情提供中断.但我也认为,虽然没有其他事情发生,但您将获得最大的计算量.

I decided to try a recursive function out, which calls itself from inside a setTimeout of 0ms. I figured that this would provide the breaks in the event loop for anything else that wanted to happen while it was running. But I also figured that while there is nothing else going on you will get maximum computation.

这是我想出来的.

(我要为代码道歉.我在控制台中进行试验,所以这又快又脏.)

(I'm going to apologise for the code. I was experimenting in the console so this was quick and dirty.)

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);
    };
}

(您可以将此代码粘贴到控制台中,它会起作用)

( You can paste this code into the console and it will work )

本质上该函数的作用是获取一个数字,创建一个数组并调用自身,而 array.length <;number 将计数推入数组.它将在第一次调用中创建的数组传递给所有后续调用.

Essentially what the function does is takes a number, creates an array and calls itself while the array.length < number pushing the count so far into the array. It passes the array created in the first call to all subsequent calls.

我对其进行了测试,它似乎完全按预期工作.只是它的性能相当差.我用...进行了测试.

I tested it out and it seems to work exactly as intended. Only it's performance is fairly poor. I tested it out with..

(这又不是性感代码)

test(5000, undefined, function(ar, start ){ 
    var finish = new Date; 
    console.log(
        ar.length,
        'timeTaken: ', finish - start 
    ); 
});

现在我显然想知道完成需要多长时间,上面的代码花了大约 20 秒.现在在我看来,JS 计数到 5000 应该不需要 20 秒.加上它正在做一些计算和处理以将项目推入数组的事实.但是20s还是有点陡峭.

Now I obviously wanted to know how long it took to complete, the above code took around 20s. Now it seems to me that it should not take 20s for JS to count to 5000. Add in the fact that it is doing some calculation and processing to push items into the array. But still 20s is a bit steep.

所以我决定同时生成几个,看看这对浏览器性能和计算速度有何影响.

So I decided to spawn several at the same time to see how that effected the browser performance and calculation speeds.

(代码没有变得更性感)

( the code isn't getting any sexier )

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'  ) });
};

所以总共五个,同时运行并且不会导致任何浏览器挂起.

So that's five in total, running at the same time and not causing any hanging of the browser.

在进程结束后,所有结果几乎在同一时间返回.所有这些都需要大约 21.5 秒才能完成.这仅比单独运行慢 1.5 秒.但是我在具有 :hover 效果的元素上移动我的鼠标只是为了确保浏览器仍在响应,所以这可能会解释一些 1.5 秒的开销.

after the process's ended the all results returned at virtually exactly the same time. it took around 21.5s for all of them to complete. That's just 1.5s slower than one on it's own. But I was moving my mouse around the window on elements that had :hover effects just to make sure that the browser was still responding, so that might account for some of the 1.5s overhead.

因此,由于这些函数显然是并行运行的,浏览器中还剩下更多的计算能力.

So as these functions are obviously running in parallel there is more computational juice left in the browser.

有没有人能解释一下这里发生的性能方面的事情,并详细说明如何改进这样的功能?

只是为了发疯,我这样做了..

Just to go crazy I did this..

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'  ) });
};

在我写这篇文章的整个过程中,它一直在运行,并且仍在继续.浏览器没有抱怨或挂起.一旦结束,我会添加完成时间.

It's been running the whole time I have been writing this post, and is still going for it. The browser is not complaining or hanging. I will add the completion time once it ends.

推荐答案

setTimeout 没有 0ms 的最小延迟.最小延迟在 5ms-20ms 范围内,具体取决于浏览器.

setTimeout does not have a minimal delay of 0ms. The minimal delay is anywhere in the range of 5ms-20ms dependent on browsers.

我自己的个人测试表明 setTimeout 不会立即将您放在事件堆栈上

My own personal testing shows that setTimeout doesn't place your back on the event stack immediately

现场示例

它在被再次调用之前有一个任意的最小时间延迟

It has an arbitary minimal time delay before it gets called again

var s = new Date(),
    count = 10000,
    cb = after(count, function() {
        console.log(new Date() - s);    
    });

doo(count, function() {
    test(10, undefined, cb);
});

  • 将其中 10000 个并行运行到 10 个需要 500 毫秒.
  • 从 100 数到 10 需要 60 毫秒.
  • 从 1 数到 10 需要 40 毫秒.
  • 从 1 数到 100 需要 400 毫秒.
  • 显然每个 setTimeout 必须等待至少 4 毫秒才能再次被调用.但这就是瓶颈.setTimeout 上的个别延迟.

    Cleary it seems that each individual setTimeout has to wait at least 4ms to be called again. But that's the bottle neck. The individual delay on setTimeout.

    如果您并行安排 100 个或更多这些,那么它就会起作用.

    If you schedule a 100 or more of these in parallel then it will just work.

    我们如何优化它?

    var s = new Date(),
        count = 100,
        cb = after(count, function() {
            console.log(new Date() - s);    
        }),
        array = [];
    
    doo(count, function() {
        test(10, array, cb);
    });
    

    设置 100 个在同一阵列上并行运行.这将避免主要瓶颈,即 setTimeout 延迟.

    Set up 100 running in parallel on the same array. This will avoid the main bottleneck which is the setTimeout delay.

    上述操作在 2 毫秒内完成.

    The above completes in 2ms.

    var s = new Date(),
        count = 1000,
        cb = after(count, function() {
            console.log(new Date() - s);    
        }),
        array = [];
    
    doo(count, function() {
        test(1000, array, cb);
    });
    

    7 毫秒内完成

    var s = new Date(),
        count = 1000,
        cb = after(1, function() {
            console.log(new Date() - s);    
        }),
        array = [];
    
    doo(count, function() {
        test(1000000, array, cb);
    });
    

    并行运行 1000 个作业是大致最佳的.但是您将开始遇到瓶颈.数到 100 万仍然需要 4500 毫秒.

    Running a 1000 jobs in parallel is roughly optimum. But you will start hitting bottlenecks. Counting to 1 million still takes 4500ms.

    这篇关于JavaScript 性能长时间运行的任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆