【问题标题】:Measure CPU Load Difference Between Four Similar Javascript Functions测量四个类似 Javascript 函数之间的 CPU 负载差异
【发布时间】:2016-06-25 02:06:33
【问题描述】:

为什么这对我很重要

我有一个网站,我需要运行一个倒数计时器,以向人们显示他们还剩下多少时间来完成一项操作。

此计时器将运行数天,并且可能只是使用 MomentJS 从 MomentJS 的 to() 函数中说出类似“4 天内”的内容。

但是,当我们还剩一个小时时,我将切换到按分钟倒计时的计时器,最终当分钟数足够低时,我将使用秒计时器。当我们进入最后几分钟时,我什至会显示毫秒。


问题

几乎有两种主要技术可以为倒数计时器设置动画。

  • setInterval()
  • requestAnimationFrame()

嗯,我马上注意到requestAnimationFrame() 方法看起来更加流畅,效果很好——尤其是在我显示毫秒时。但是没过多久,我发现我可怜的电脑开始有点发热。所有这些“动画”都对我的 CPU 造成了相当大的负担。我尝试使用 CPU 监视器,并环顾四周,看看这会给我的 CPU 带来多少负载,但总的来说,我真的找不到一个工具可以清楚地显示我的小倒计时计时器是哪种 CPU 负载使用。

所以,我决定找到一种方法来限制 FPS,看看这是否会减少我的问题。是的,确实如此。如果您将setTimeout()requestAnimationFrame() 结合使用,您可以在调用下一个函数之前设置等待时间。

如果您使用的是setTimeout(),这就提出了一个问题——为什么不直接使用setInterval() 而忘记requestAnimationFrame() 为您提供的额外优化?

我环顾四周,发现了另一种方法,它只是检查自上次requestAnimationFrame() 调用您的函数以来是否经过了正确的间隔时间。我对这段代码的工作方式进行了一些优化,最终得到了我在下面尝试测量的两个函数之一。

最后,我真的很想有一个更清晰的方法来衡量这个 - 因为我的 mac 上的活动监视器几乎不是提供准确读数的可靠工具 - 我找不到一种方法来衡量我正在运行的代码。

Chrome 有更多工具、分析器和时间线 - 它们都非常有用,但它们没有提供我正在寻找的指标 - CPU 负载。


代码:

这里有四个代码 sn-ps,它们的作用完全相同——它们都使用:

  • MomentJS
  • CountdownJS
  • jQuery

代码是 100% 相同的,唯一的区别是我如何限制动画的 FPS。

我想找到一种方法来衡量(尽可能精确地)这四个函数在它们占用的 CPU 负载方面的差异。然后我想更改 FPS 以查看是否可以为我的应用程序找到可接受的负载,然后我可以找到最佳点 - 在不同的计时器阶段,正确的 FPS 数量。

技巧 1 - setTimeout()

var now = moment(); // new Date().getTime();
var then = moment().add(60, 'seconds'); // new Date(now + 60 * 1000);

$(".now").text(moment(now).format('h:mm:ss a'));
$(".then").text(moment(then).format('h:mm:ss a'));
$(".duration").text(moment(now).to(then));

(function timerLoop() {
  setTimeout(function(){
    $(".difference").text(moment().to(then));
    $(".countdown").text(countdown(then, null, countdown.YEARS | countdown.MONTHS | countdown.DAYS | countdown.HOURS | countdown.MINUTES | countdown.SECONDS | countdown.MILLISECONDS).toString());
    requestAnimationFrame(timerLoop);
  }, 1000/30);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js" type="text/javascript"></script>
<script src="https://cdn.rawgit.com/mckamey/countdownjs/master/countdown.min.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" type="text/javascript"></script>
<div>
  The time is now: <span class="now"></span>, a timer will go off <span class="duration"></span> at <span class="then"></span>
</div>
<div>The timer is set to go off <span class="difference"></span></div>
<div class="countdown"></div>

技术 2 - 间隔之间的增量

var now = moment(); // new Date().getTime();
var then = moment().add(60, 'seconds'); // new Date(now + 60 * 1000);

$(".now").text(moment(now).format('h:mm:ss a'));
$(".then").text(moment(then).format('h:mm:ss a'));
$(".duration").text(moment(now).to(then));

var fps = 30;
var interval = 1000/fps;
var performanceTime = performance.now();
var performanceDelta;

(function updateCountdown(time) {
  performanceDelta = time - performanceTime;
  if (performanceDelta > interval) {
    performanceTime = time - (performanceDelta % interval);
    $(".difference").text(moment().to(then));
    $(".countdown").text(countdown(then, null, countdown.YEARS | countdown.MONTHS | countdown.DAYS | countdown.HOURS | countdown.MINUTES | countdown.SECONDS | countdown.MILLISECONDS).toString());
  }
  requestAnimationFrame(updateCountdown);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js" type="text/javascript"></script>
<script src="https://cdn.rawgit.com/mckamey/countdownjs/master/countdown.min.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" type="text/javascript"></script>
<div>
  The time is now: <span class="now"></span>, a timer will go off <span class="duration"></span> at <span class="then"></span>
</div>
<div>The timer is set to go off <span class="difference"></span></div>
<div class="countdown"></div>

技巧 3 - setInterval()

var now = moment(); // new Date().getTime();
var then = moment().add(60, 'seconds'); // new Date(now + 60 * 1000);

$(".now").text(moment(now).format('h:mm:ss a'));
$(".then").text(moment(then).format('h:mm:ss a'));
$(".duration").text(moment(now).to(then));

var fps = 30;
var interval = 1000/fps;

setInterval(function updateCountdown() {
    $(".difference").text(moment().to(then));
    $(".countdown").text(countdown(then, null, countdown.YEARS | countdown.MONTHS | countdown.DAYS | countdown.HOURS | countdown.MINUTES | countdown.SECONDS | countdown.MILLISECONDS).toString());
}, interval);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js" type="text/javascript"></script>
<script src="https://cdn.rawgit.com/mckamey/countdownjs/master/countdown.min.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" type="text/javascript"></script>
<div>
  The time is now: <span class="now"></span>, a timer will go off <span class="duration"></span> at <span class="then"></span>
</div>
<div>The timer is set to go off <span class="difference"></span></div>
<div class="countdown"></div>

看到像这样完全不受限制的版本也会很有趣:

技巧 4 - 无油门

var now = moment(); // new Date().getTime();
var then = moment().add(60, 'seconds'); // new Date(now + 60 * 1000);

$(".now").text(moment(now).format('h:mm:ss a'));
$(".then").text(moment(then).format('h:mm:ss a'));
$(".duration").text(moment(now).to(then));
(function timerLoop() {
  $(".difference").text(moment().to(then));
  $(".countdown").text(countdown(then, null, countdown.YEARS | countdown.MONTHS | countdown.DAYS | countdown.HOURS | countdown.MINUTES | countdown.SECONDS | countdown.MILLISECONDS).toString());
  requestAnimationFrame(timerLoop);
})();

// CountdownJS: http://countdownjs.org/
// MomentJS: http://momentjs.com/
// jQuery: https://jquery.com/
// Rawgit: http://rawgit.com/
// Light reading about the requestAnimationFrame pattern:
// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
// https://css-tricks.com/using-requestanimationframe/
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js" type="text/javascript"></script>
<script src="https://cdn.rawgit.com/mckamey/countdownjs/master/countdown.min.js" type="text/javascript"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" type="text/javascript"></script>
<div>
  The time is now: <span class="now"></span>, a timer will go off <span class="duration"></span> at <span class="then"></span>
</div>
<div>The timer is set to go off <span class="difference"></span></div>
<div class="countdown"></div>

简而言之:如何衡量四个类似 javascript 函数之间的 CPU 负载差异?

是否有人已经知道其中哪一个会更性能? (我知道这不是真的一个词)

【问题讨论】:

    标签: javascript performance settimeout setinterval requestanimationframe


    【解决方案1】:

    回答我自己的问题:

    简短的回答:

    • 最差表现

      显然setInterval() 是最糟糕的解决方案。因为setInterval() 仍然在您不在选项卡上时运行,从而浪费 CPU 和电池寿命。

    • 最佳动画(微秒)

      显然Delta Interval Math计算方法是计算间隔时间最平滑、最准确的方法。当您将此算法与使用 performance.now() 计算帧时间的准确性相结合时,您可以通过动画帧获得精确到微秒的结果。

      (是的,即使requestAnimationFrame() 使用performance.now() 时间作为它传递给回调函数的第一个参数)

      是的,伙计们,我的意思是微秒。即 1s/1000ms/1000µs。

      来吧,现在测试一下。打开你的控制台并输入:performance.now() 你会得到一个看起来像2132438.165 的数字——这是自浏览器渲染第一帧以来的毫秒数。

      (这很酷,因为 µ 是一个希腊字符 +10 nerd points

    • 最佳 CPU 性能(毫秒)

      结合requestAnimationFrame()(当你切换标签时让你的动画进入休眠状态)setTimeout(),它可以将你的动画的FPS限制在任何所需的毫秒间隔。

      但请记住,此方法与 Delta Interval Math 方法之间的区别非常非常细微。我什至没有办法量化差异有多大。据我所知,它的效率可能高出四分之一到八分之一。但是您会因此而失去很多平滑度。你的选择。


    长答案:

    我仍然期待有更好的方法来做到这一点,也许可以让我比较不同函数之间的数据。

    在那之前,我能够使用Google's Javascript CPU Profiler 生成这些图像

    按照我认为它们表现出色的顺序列出,但标题与原始问题匹配:

    技巧 3 - setInterval()

    技巧 1 - setTimeout()

    技术 2 - 间隔之间的增量

    技巧 4 - 无油门


    视觉分析

    好吧,从外观上看,我会按照性能顺序对不同的功能进行排名:

    1. setInterval() 函数。
    2. setTimeout() wrapping therequestAnimationFrame()` 调用。
    3. Delta Interval MathrequestAnimationFrame() 调用的递归函数中
    4. 没有 FPS Throttle 仅循环使用 requestAnimationFrame()

    结论:[编辑:06/25/2016]

    setInterval() 函数优于我发现的任何 requestAnimationFrame() 模式,至少出于 CPU 使用率的原因仅当您在选项卡上时

    因此,如果 CPU 成本 或更长的电池寿命 是您的应用程序更关心的问题,那么请使用 requestAnimationFrame() 并使用以下任一方法来限制 FPS:

    • setTimeout() 方法似乎对 CPU 的工作量要少一些(两全其美) - 但诚然不如下面的方法流畅。
    • Delta Interval Math 接缝使动画更加平滑(并使用此问题/答案中概述的技术,时间来自performance.now() 和来自requestAnimationFrame() 报告的时间(这只是当前performance.now() 在回调函数的第一个参数中提供)。因为我们使用的是我见过的最准确的算法来计算动画帧(你实际上是在计算到百万分之一秒,或者微秒 1s/1000ms/1000µs)。这种技术和 setTimeout() 的 CPU 负载没有太大差异 - 但有区别(参考附图)

    为什么 requestAnimationFrame() 是数字炸弹?” - 因为:当你不在那个标签上时,它会被浏览器关闭,所以您的动画正在“等待”您回来。并且使用 performance.now() 您将获得微秒 (µs) 的动画精度。

    It is said requestAnimationFrame() 是一种“优化”方式,可确保您的动画与其他浏览器重绘一起工作,以便您的动画“适合”浏览器正在执行的操作,它还需要 60FPS 回调成本。


    我生成这些照片的步骤:

    1. 创建了单独的空白 HTML 文件,其中只有我测试所需的内容。
    2. 重新启动我的电脑,只打开我的 chrome 浏览器到 about:blank 选项卡。
    3. 打开我之前单独创建的每个“测试基准”HTML 文件,一次一个。
    4. 在倒计时 50 秒后,我在 Google's Javascript CPU Profiler 上单击了 start,10 秒后,我单击了 stop(我使用了一个内置在我的 gamer-mouse's 中的特殊单击宏,特别是 driver software,效果很好好吧 - 一旦我点击按钮,它就会点击 start 并在 10 秒后再次点击 stop - 对于这些照片来说已经足够了)
    5. 已将配置文件保存到我的计算机中
    6. 已将每个配置文件加载到about:blank 选项卡上的分析器工具中。
    7. 调整窗口大小以更好地框住数据
    8. 屏幕截图[shift] + [cmd] + [4],然后按键盘上的space 将屏幕截图精确地框在窗口周围。

    未解决的问题:

    我仍然无法查看 CPU % 使用率是多少。这张图让我想要更详细的东西。其他Google Javascript CPU Profiler 屏幕在这个意义上也有点模糊。也许我只是不知道如何使用该工具。

    如果有人知道更好的测试基准,请发送答案,我会审查它。我希望它比这个烂摊子更好。并提前致谢。

    这里的主要问题是有一种方法可以量化从一个 CPU 配置文件到另一个 CPU 配置文件的硬数据并进行比较。这是我最想念的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 2021-03-27
      • 2022-07-06
      • 2013-12-06
      相关资源
      最近更新 更多