精确控制和测量图像显示的时间

26
为了进行一个反应时间研究(如果您感兴趣,请参阅此问题),我们想要控制和测量图像的显示时间。我们希望考虑到在不同用户的机器上重新绘制所需的时间。 编辑:最初,我只使用内联执行来计时,并认为我不能相信它可以准确地测量图片在用户屏幕上可见的时间,因为绘制需要一些时间。
后来,我发现了事件 "MozAfterPaint"。它需要对用户计算机进行配置更改,而相应的 WebkitAfterPaint 没有成功。这意味着我无法在用户计算机上使用它,但我用它进行了自己的测试。我贴出了相关的代码片段和我的测试结果。
我还在 Chrome 中使用 SpeedTracer 手动检查了结果。
// from the loop pre-rendering images for faster display
var imgdiv = $('<div class="trial_images" id="trial_images_'+i+'" style="display:none"><img class="top" src="' + toppath + '"><br><img class="bottom" src="'+ botpath + '"></div>');
Session.imgs[i] = imgdiv.append(botimg);
$('#trial').append(Session.imgs);

// in Trial.showImages
$(window).one('MozAfterPaint', function () {
    Trial.FixationHidden = performance.now();
});
$('#trial_images_'+Trial.current).show(); // this would cause reflows, but I've since changed it to use the visibility property and absolutely positioned images, to minimise reflows
Trial.ImagesShown = performance.now();

Session.waitForNextStep = setTimeout(Trial.showProbe, 500); // 500ms    

// in Trial.showProbe
$(window).one('MozAfterPaint', function () {
    Trial.ImagesHidden = performance.now();
});
$('#trial_images_'+Trial.current).hide();
Trial.ProbeShown = performance.now();
// show Probe etc...

MozAfterPaint和内联执行测量时间的比较结果。

这让我感到有些不满意。首先,中位数显示时间比我想要的短约30ms。其次,使用MozAfterPaint的方差相当大(比内联执行更大),因此我不能仅通过增加setTimeout 30ms来调整它。第三,这是在我的相对快速的计算机上得出的结果,其他计算机的结果可能更差。

Boxplot of durations

Relationship of durations measured using two methods

SpeedTracer的结果

这些结果比较好。图像可见时间通常在预期持续时间内,最多有4毫秒或10毫秒的偏差。此外,Chrome在setTimeout调用时考虑了重新绘制所需的时间(如果需要重新绘制图像,则调用时间会相差504毫秒)。 不幸的是,我无法分析和绘制SpeedTracer中许多试验的结果,因为它只记录在控制台中。我不确定SpeedTracer和MozAfterPaint之间的差异是反映了两个浏览器的差异还是我的MozAfterPaint使用中的缺陷(我相当确定我正确解释了SpeedTracer输出)。

问题

我想知道:

  1. 我该如何测量它在用户机器上实际可见的时间,或者至少获取一组不同浏览器在不同测试计算机上的可比较数字(Chrome,Firefox,Safari)?
  2. 我能否抵消渲染和绘制时间,以达到500毫秒的实际可见性?如果我必须依赖于通用偏移量,那将更糟,但仍然比在相对较慢的计算机上以如此短的时间显示图像好。
  3. 我们使用setTimeout。 我知道requestAnimationFrame,但似乎我们无法从中获得任何好处:
    该研究应在整个研究期间关注,重要的是我们获得+/- 500ms的显示时间,而不是确定数量的fps。 我的理解正确吗?
很显然,JavaScript并不是最理想的选择,但对于我们的目的来说,它是最好的选择(这项研究必须在线上运行在用户自己的电脑上,要求他们安装某些东西会吓到一些人,而Java已经不再捆绑在Mac OS X浏览器中)。目前,我们只允许使用当前版本的Safari、Chrome、Firefox和可能的MSIE(性能检测功能.now和全屏API,我还没有检查过MSIE的表现)。

由于浏览器不管如何隐藏/显示图像都必须重新绘制,所以我认为这只是一个“最不可怕”的情况。也就是说,对浏览器窗口的任何更改都将导致重新绘制(尽管可能仅限于图像所在的某些区域)。每个浏览器都会以不同的方式执行此操作,而且每台计算机也都如此。我认为您接受的显示时间差异可能需要扩展以使用HTML/CSS/JS解决方案。 - Jordan Kasper
@patrick 你是指这些原则,具体是什么意思?我不能说明所有的事情,但我会尽力说明我能够说明的。如果只是关于测量偏差的问题,那么这些偏差会平均分布。但是,如果某些用户显示的图像时间太短,他们根本就不会得到处理,我希望尽可能避免这种情况。 - Ruben
@patrick 容差级别的想法很好。一个已删除的答案链接到了一篇博客文章,其中描述了如何解决不再存在于performance.now中的不同问题(非单调时间,系统时钟轮询问题)。我的容差级别大约是绘制是否太长。但是我如何找出我的容差级别是否被违反?这归结为1,对吧? - Ruben
@patrick 我想要弄清楚在几个配置上演示时间的差异(最好是在每个用户的计算机上,但我怀疑这不会奏效)。我正在尝试找出我的估计是否有误,如何获得更好的估计,以及是否以及如何减少预期和实际演示时间之间的差异。如果我认为上述30毫秒甚至更多的差异无关紧要,我就不会付出那么多努力。 - Ruben
你尝试过benchmark.js吗?它使用了一些与jsperf和其他“测试站点”相同的技术,通常会展示一个Java小程序(如果可用),以获取纳秒等信息。 - adeneo
显示剩余6条评论
2个回答

5
因为我还没有得到更多的答案,但在编辑问题时学到了很多东西,所以我将我的进展作为答案发布。正如您所看到的,它仍然不是最佳的,我很乐意把赏金授予任何能够改进它的人。

统计数据

New results

  • 在最左边的面板中,您可以看到分布情况,这让我开始怀疑我得到的时间估计。
  • 中间面板显示了我缓存选择器、重新排序某些调用、使用更多链接、通过使用visibility和绝对定位而不是display来最小化回流所实现的效果。
  • 最右边的面板显示了我使用Joe Lambert适应的函数,在阅读了一篇关于rAF现在也具有亚毫秒精度的博客文章之后。我认为它只会帮助我平滑动画,但显然它也有助于获得更好的实际显示持续时间。

结果

在最终面板中,“paint-to-paint”定时的平均值约为500ms,内联执行定时的平均值散布合理(因为我使用相同的时间戳来终止下面的内部循环),并与“paint-to-paint”定时相关。

这些时间长度仍有相当大的差异,我希望进一步减少它,但这绝对是进步。我需要在一些较慢和一些Windows计算机上进行测试,以确定是否真正满意,最初我希望所有偏差都在10毫秒以下。

如果我制作一个不需要用户交互的测试套件,我也可以收集更多的数据,但我想在我们实际的应用程序中进行,以获得现实估计。

window.requestAnimationFrame使用window.requestTimeout

window.requestTimeout = function(fn, delay) {
    var start = performance.now(),
        handle = new Object();
    function loop(){
        var current = performance.now(),
            delta = current - start;

        delta >= delay ? fn.call() : handle.value = window.requestAnimationFrame(loop);
    };
    handle.value = window.requestAnimationFrame(loop);
    return handle;
};

编辑:

我的另一个问题的答案链接到了一篇好的新文章


4

你是否尝试获取初始毫秒数,并在事件触发后计算差异?而不是使用setTimeout。类似这样:

var startDate = new Date();
var startMilliseconds = startDate.getTime();

// when the event is fired :
(...), function() {
    console.log(new Date().getTime() - startMilliseconds);
});

尽可能避免使用jQuery,纯JS将为您提供更好的响应时间和更好的整体性能。

是的,我也这样做了。它在我的代码片段中,那就是箱线图右半部分的来源。我只是使用performance.now而不是new Date().getTime(),因为它更准确(请参见我上面链接的另一个问题)。我认为我使用jQuery不会明显影响性能,你确定吗?据我所知,我没有使用任何掩盖极其复杂行为的函数,并且JS执行时间从未超过0.x毫秒。绘制事件似乎是我可以改进的地方。 - Ruben
取决于选择器上下文和DOM树的大小。请尝试查看此网站以获取进一步的改进:http://net.tutsplus.com/tutorials/javascript-ajax/10-ways-to-instantly-increase-your-jquery-performance/ 干杯 - canolucas
虽然我会写一个不用jQuery的小代码片段,看看它是否与jQuery类似。我真的会尝试一下。也许这是事件被触发时的瓶颈时间。 - canolucas
谢谢提供链接,我会进行比较。但是这肯定不会帮助我改进或衡量SpeedTracer给出的绘制时间。 - Ruben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接