测量四个类似Javascript函数之间的CPU负载差异

Jer*_*art 8 javascript performance settimeout setinterval requestanimationframe

为什么这对我很重要

我有一个网站,我需要运行一个倒数计时器来向人们展示他们完成一个动作需要多少时间.

这个计时器将运行几天,可能只是使用MomentJS来说出MomentJS to()功能中的"4天内" .

然而,当我们还剩一个小时的时候,我将按分钟计时器切换到倒计时,最终当分钟变得足够低时,我将涉及一个秒计时器.当我们到最后几分钟时,我甚至会显示几毫秒.


问题

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

  • setInterval()
  • requestAnimationFrame()

好吧,我立刻注意到这个requestAnimationFrame()方法对眼睛更加平滑,效果很好 - 尤其是当我显示毫秒时.然而,当我注意到我可怜的电脑开始变得温暖时,不久.所有这些"动画"都会给我的CPU造成很大负担.我尝试使用CPU监视器,并查看了看看我的CPU有多少负载的方法,但总的来说,我找不到一个工具,可以让我清楚地了解我的小倒计时器是什么样的CPU负载.使用.

所以,我决定找到一种限制 FPS的方法,看看是否会减少我的问题.是的,确实如此.如果您同时使用setTimeout(),则requestAnimationFrame()可以在调用下一个功能之前设置等待时间.

这提出了一个问题,如果你正在使用setTimeout()- 你为什么不使用setInterval()并忘记requestAnimationFrame()给你的额外优化?

我做了一些环顾四周,找到了另一种方法,只需检查自上次requestAnimationFrame()调用函数以来是否已经过了正确的间隔时间.我对这段代码的工作方式做了一些优化,最后得到了我试图在下面测量的两个函数之一.

最后,我真的希望有一个更清晰的方法来衡量这一点 - 因为我的mac上的活动监视器不是提供准确读数的可靠工具 - 而且我找不到一种方法来测量代码我跑了.

Chrome有一些工具,分析器和时间线 - 这些都非常有用,但它们没有给我我正在寻找的指标 - CPU负载.


代码:

这里有四个代码片段,它们完全相同 - 所有这些代码片段都使用:

  • 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);
})();
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

技术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);
})();
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

技术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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

看到完全不受限制的版本也是很有趣的:

技术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/
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


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

有没有人知道哪一个会更高效?(我知道这是不是真的一个字)

Jer*_*art 4

回答我自己的问题:

\n\n

简短的回答:

\n\n
    \n
  • 最差表现

    \n\n
    \n

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

    \n
  • \n
  • 最佳动画(微秒)

    \n\n
    \n

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

    \n\n

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

    \n\n

    是的,朋友们,我真正的意思是微秒。即 1s/1000ms/1000\xc2\xb5s。

    \n\n

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

    \n\n

    (这特别酷,因为 \xc2\xb5 是希腊字符+10 nerd points

    \n
  • \n
  • 最佳 CPU 性能(毫秒)

    \n\n
    \n

    组合requestAnimationFrame() (允许您的动画在切换选项卡时休眠)setTimeout()可以将动画的 FPS 限制为任何所需的毫秒间隔。

    \n\n

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

    \n
  • \n
\n\n
\n\n

长答案:

\n\n

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

\n\n

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

\n\n

按照我认为它们有效的顺序列出,但标题与原始问题相匹配:

\n\n

技术 3 - setInterval() \n技术 3 - setInterval()

\n\n

技术 1 - setTimeout() \n技术 1 - setTimeout()

\n\n

技术 2 - 间隔之间的增量\n技术 2 - 间隔之间的增量

\n\n

技巧 4 - 无节流\n技巧 4 - 无油门

\n\n
\n\n

视觉分析

\n\n

好吧,从表面上看,我会按照以下性能顺序对不同的函数进行排名:

\n\n
    \n
  1. setInterval()功能。
  2. \n
  3. setTimeout() wrapping therequestAnimationFrame()` 调用。
  4. \n
  5. 调用的递归函数内的Delta Interval MathrequestAnimationFrame()
  6. \n
  7. 没有 FPS 油门,只是循环使用requestAnimationFrame()
  8. \n
\n\n

结论:[编辑:2016 年 6 月 25 日]

\n\n

仅当您在选项卡上时,该功能才比我发现的setInterval()任何模式都要好,至少出于 CPU 使用原因。requestAnimationFrame()

\n\n

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

\n\n
    \n
  • setTimeout()方法似乎对 CPU 的工作量要少一些(两全其美)——但无可否认,不如下面的方法顺利。
  • \n
  • 或者
  • \n
  • 动画Delta Interval Math看起来更加平滑(并使用此问题/答案中的技术概述以及时间performance.now()和报告的时间requestAnimationFrame()(这只是performance.now()回调函数的第一个参数中提供的当前时间)因为我们使用的是我所见过的最准确的算法来计算动画帧(实际上计算到百万分之一秒,或一微秒 1s/1000ms/1000\xc2\xb5s)。这种技术的 CPU 负载并没有很大的差异setTimeout()- 但有一个差异(参考附图)
  • \n
\n\n

为什么是requestAnimationFrame()炸弹数字?” -因为:当您不在该选项卡上时,它会被浏览器关闭,因此您的动画正在“等待”您回来。并且使用 Performance.now()您的动画将获得微秒 (\xc2\xb5s) 的精度。

\n\n

据说requestAnimationFrame()是一种“优化”方法,可确保您的动画与其他浏览器重绘一起工作,以便您的动画“适合”浏览器正在执行的操作,但它的回调成本也为 60FPS。

\n\n
\n\n

我生成这些照片所采取的步骤:

\n\n
    \n
  1. 创建了单独的空白 HTML 文件,其中没有任何内容,但绝对是我测试所需的。
  2. \n
  3. 重新启动我的电脑,只打开我的 Chrome 浏览器的一个about:blank选项卡。
  4. \n
  5. 打开我之前单独创建的每个“测试基准”HTML 文件,一次一个。
  6. \n
  7. 在倒计时正好 50 秒时,我点击startGoogle 的 Javascript CPU Profiler,正好 10 秒后,我点击了(我使用了内置于我的玩家鼠标的特殊驱动程序软件stop中的特殊点击宏,效果非常好 - 一次我点击了它所点击的按钮,10 秒后再次点击- 对于这些照片来说已经足够了)startstop
  8. \n
  9. 已将配置文件保存到我的计算机
  10. \n
  11. 将每个配置文件加载到about:blank选项卡上的分析器工具中。
  12. \n
  13. 调整窗口大小以更好地构建数据
  14. \n
  15. 截屏[shift] + [cmd] + [4],然后按space键盘将截屏精确地框在窗口周围。
  16. \n
\n\n
\n\n

未解决的问题:

\n\n

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

\n\n

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

\n\n

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

\n