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

查看:165
本文介绍了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 ms,这样它就可以让用户继续与页面进行交互处理之间。他还考虑使用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.

我记得看过Douglas Crockford的视频在js conf 。该视频与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.

I决定尝试一个递归函数out,它从0ms的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..

(再次这不是性感代码)

( again this is not sexy code )

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

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

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.

(代码没有任何性感)

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.5s中的一些开销。

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

实例

Live Example

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

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个需要500毫秒。

  • 运行100计数到10需要60ms。

  • 运行1计数到10需要40ms。

  • 运行1计数到100需要400毫秒。

    • Running 10000 of these in parallel counting to 10 takes 500ms.
    • Running 100 counting to 10 takes 60ms.
    • Running 1 counting to 10 takes 40ms.
    • Running 1 counting to 100 takes 400ms.
    • 似乎每个人<$ li c $ c> setTimeout 必须等待至少4ms才能再次调用。但那是瓶颈。 上的个别延迟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.

      上述完成时间为2ms。

      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天全站免登陆