【问题标题】:How to maximize performance of an asynchronous scheduler?如何最大化异步调度器的性能?
【发布时间】:2016-08-08 16:47:12
【问题描述】:

我注意到 JavaScript 中有一种奇怪的技术,我用它来提高在画布上绘制的重复过程的性能。将来,我计划在实现时使用SharedBufffer 甚至SharedCanvas,但与此同时,这是我使用的总体思路:

function someContinuousProcess(intervals, delay) {
  var count = 0;
  var span = document.querySelector('span');

  function someExpensiveFunction() {
    if (count >= 1e9) {
      return false;
    }

    do {
      count++;
    } while (count % 1e5);

    span.textContent = count;
    
    return true;
  }
  
  function wrapper(index) {
    var start = performance.now();
    
    if (someExpensiveFunction()) {
      var delta = performance.now() - start;
      // some logic here to determine new 
      // values for `intervals` and `delay`
      scheduler[index] = setTimeout(
        wrapper.bind(null, index),
        delay
      );
    }
  }

  var scheduler = [];

  function startScheduler() {
    for (var i = 0; i < intervals; i++) {
      scheduler[i] = setTimeout(
        wrapper.bind(null, i),
        delay
      );
    }
  }

  function stopScheduler() {
    for (var i = 0; i < scheduler.length; i++) {
      clearTimeout(scheduler[i]);
    }

    scheduler = [];
  }

  startScheduler();
}

int.onchange = del.onchange = () => {
  var intervals = parseInt(int.value);
  var delay     = parseInt(del.value);

  if (!isNaN(intervals) && !isNaN(delay)) {
    someContinuousProcess(intervals, delay);
  }
};
<input placeholder=intervals id=int>
<input placeholder=delay id=del>
<span></span>

如果你弄乱了参数,你会发现delayintervals 当然都会对性能产生显着影响。但在一定程度上,如果你设置过多的intervals 以达到一定的延迟,性能提升会变成性能下降,同时导致线程无响应。

我的问题是,是否可以根据给定的someExpensiveFunction 自适应地选择intervalsdelay?假设我们可以让someExpensiveFunction() 返回一个高分辨率时间戳performance.now(),那么我们如何使用它来智能地改变intervalsdelay 以优化性能?

【问题讨论】:

  • 您希望使用 setTimeout 而不是 setInterval,以便间隔的持续时间可以随时间变化。此外,正确使用 setTimeout 可以防止您一次堆叠大量操作。 (如果你在 setInterval 中的逻辑超过了区间,那你就麻烦了。)
  • @ChristopherDavies 如果您愿意,我可以重写调度程序以使用setTimeout,但这并不能真正回答我的主要问题,即如何以编程方式定位最佳值。另外,如果setInterval 中的逻辑超过了间隔,如果你仔细这样做,我发现这就是提高性能的方法。我的猜测是 JavaScript 引擎以某种方式提示 CPU 超频或在这些条件下进行某些操作,但正如我尝试暗示的那样,不同的计算机和浏览器之间的特定值会有所不同。
  • 这里有一个很好的链接来说明为什么应该使用 setTimeout:zetafleet.com/blog/2010/04/… 至于你的主要问题,我将发表后续评论。
  • 您可以让昂贵的函数更新某些状态(例如durationOfLastComputation 或类似的东西)。然后,您将根据durationOfLastComputation 确定超时持续时间。但是,如果您使用 setTimeout 而不是间隔,则可能不必这样做,因为根据定义,如果您从超时回调中调用 expensiveFunction,则在执行之前不会发生重新调度。跨度>
  • @ChristopherDavies 我更新了我的代码 sn-p 以根据您的要求使用 setTimeout

标签: javascript performance asynchronous optimization micro-optimization


【解决方案1】:

这似乎是使用网络工作者的绝佳机会。它们在单独的线程中执行,因此不会影响主线程上的动画。

但是,仍然要求您不要太频繁地postMessage,否则主线程中的事件循环会因消息数量过多而窒息。

function simple() {
  var span = $('#v1');
  var worker = new Worker('data:text/javascript;charset=UTF-8,' + encodeURI($('#someExpensiveFunction').text()));

  worker.onmessage = progress => span.text(progress.data);
  worker.onerror = x => {
debugger;
  };
}

function withanim() {
  var span = $('#v2');
  var worker = new Worker('data:text/javascript;charset=UTF-8,' + encodeURI($('#someExpensiveFunction').text()));

  var latestMessage = 0;
  worker.onmessage = progress => latestMessage = progress.data;
  worker.onerror = x => {
debugger;
  };

  var myReq;
  function step(timestamp) {
span.text(latestMessage);
if (latestMessage < 1e9)
  myReq = requestAnimationFrame(step);
  }
  myReq = requestAnimationFrame(step);
}

simple();
withanim();
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js" 
type="text/javascript" charset="utf-8"></script>
<div id=v1></div>
<div id=v2></div>
<script type="text/worker" id="someExpensiveFunction">
var count = 0;
var start = performance.now();
var reportPerSec = 100; //change this
var delay = 1000/reportPerSec;
do {
  //some un-interrupable task
  //it might be useful to put this in a setTimeout(0), if you also want to use onmessage to get data to work on from the main thread
do {
count++;
  } while (count % 1e2);
  
  //report progress
  if (performance.now() - start > delay) {
 	postMessage(count);
start = performance.now();
  }
} while (count < 1e9);
postMessage(count); //make sure to send 'done'
</script>

Fiddle

【讨论】:

  • 我以前用过网络工作者。瓶颈最终是消息堆积起来,并且没有像生成它们一样快地被分派到 DOM 线程。我相信这个答案可能对其他人有用
  • 这就是reportPerSec的原因。这与您在重载事件循环的非网络工作者场景中遇到的问题相同。除此之外,您还可以满足主要代码。
猜你喜欢
  • 1970-01-01
  • 2010-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-02
  • 1970-01-01
  • 2017-12-08
相关资源
最近更新 更多