JavaScript性能评估

测量执行功能所花费的时间是证明一种机制的一种实现比另一种机制更具生产力的好方法。这使您可以确保在对代码进行一些更改后,函数的性能没有受到影响。它还有助于查找应用程序性能瓶颈。

如果Web项目具有高性能,则有助于用户对其进行积极的评价。如果用户喜欢使用该资源,则他们具有返回的属性。例如在这个该研究表明,有88%的在线客户不太可能返回他们遇到的不便之处。这些不便之处很可能是性能问题引起的。

这就是为什么有助于发现性能瓶颈和衡量对代码进行改进的结果的工具在Web开发中很重要的原因。这些工具在JavaScript开发中尤其重要。重要的是要知道,JavaScript是一种单线程语言,因此每一行JavaScript代码都可能会阻塞DOM。 在本文中,我将讨论如何测量功能的性能以及如何处理测量结果。





如果您认为某些计算过于繁琐而无法在主线程中执行,那么您可以决定将其移至服务工作者或网络工作者。

Performance.now()方法


通过Performance接口,可以通过方法访问DOMHighResTimeStamp类型的值performance.now()此方法返回一个时间戳,指示自文档开始存在以来经过的时间(以毫秒为单位)。此外,该指示器的精度约为5微秒(毫秒的分数)。

为了使用该方法测量代码片段的性能performance.now(),您需要执行两次时间测量,将这些测量的结果保存在变量中,然后从第二个测量的结果中减去第一个的结果:

const t0 = performance.now();
for (let i = 0; i < array.length; i++) 
{
  // - 
}
const t1 = performance.now();
console.log(t1 - t0, 'milliseconds');

在Chrome中,执行以下代码后,您将获得以下内容:

0.6350000001020817 "milliseconds"

在Firefox中,如下所示:

1 milliseconds

如您所见,在不同的浏览器中获得的测量结果非常不同。事实是,在Firefox 60中,Performance API返回的结果的准确性降低了。我们将讨论更多。

Performance接口具有的功能远不只是返回特定的时间戳。这些包括测量性能的各个方面,这些方面由该接口的扩展(例如,性能时间轴 API 导航时序用户时序资源时序)表示这是查找更多有关这些API 材料。

在我们的案例中,我们正在谈论衡量功能的性能,因此我们有足够的机会使该方法带来performance.now()

Date.now()和performance.now()


在这里,您可能会想到可以使用该方法来衡量性能Date.now()确实有可能,但是这种方法有缺点。

该方法Date.now()返回自Unix时代(1970-01-01T00:00:00Z)以来经过的时间(以毫秒为单位),并取决于系统时钟。这意味着不仅该方法不如该方法准确performance.now(),而且相反performance.now(),它返回的值在某些条件下可能基于错误的时钟指示器。这是与WebKit引擎相关的程序员Rich Gentlekor所说的:“也许程序员不太可能认为访问时返回了读数Date根据系统时间,绝对不可能理想地监视实际应用程序。大多数系统都有一个定期同步时间的守护程序。通常每15-20分钟将系统时钟调整几毫秒。用这样的频率,大约10秒间隔的测量中的1%的时钟设置将是不准确的。”

Console.time()方法


使用此API进行时间测量非常简单。足够的,在需要评估其性能的代码之前,调用方法console.time(),然后在此代码之后-方法console.timeEnd()在这种情况下,一个和另一个方法需要传递相同的字符串参数。在一页上,最多可以同时使用10,000个此类计时器。

使用此API进行时间测量的准确性与使用Performance API相同,但是在每种特定情况下所能达到的准确性取决于浏览器。

console.time('test');
for (let i = 0; i < array.length; i++) {
  // - 
}
console.timeEnd('test');

执行此类代码后,系统将自动向控制台输出有关经过时间的信息。

在Chrome浏览器中,它将类似于以下内容:

test: 0.766845703125ms

在Firefox中,如下所示:

test: 2ms - timer ended

实际上,这里的一切与我们在使用时所看到的非常相似performance.now()

该方法的优势在于console.time()易于使用。即,我们正在谈论的是它的应用不需要声明辅助变量的事实,并发现记录在其中的指标之间的差异。

时间精度降低


如果您使用上述工具测量了不同浏览器中代码的性能,那么您可能会注意到测量结果可能会有所不同的事实。

这样做的原因是浏览器试图保护用户免受基于时间的攻击和浏览器标识机制(“ 浏览器指纹”)的侵害如果时间测量的结果过于精确,则可能使攻击者有机会识别例如用户。

如前所述,在Firefox 60中,时间测量结果的准确性降低了这是通过将属性privacy.reduceTimerPrecision设置为2 ms来完成的。

测试性能时要记住的一点


现在,您可以使用各种工具来衡量JavaScript函数的性能。但是在开始业务之前,您需要考虑一些我们现在将要讨论的功能。

▍分而治之


假设过滤了一些数据,您就注意到了应用程序的缓慢运行。但是您不确切知道性能瓶颈在哪里。

与其猜测哪一部分代码运行缓慢,不如使用上述方法找出更好的方法。

为了查看正在发生的情况的一般情况,您首先需要使用console.time()console.timeEnd()评估代码块的性能,这大概会对性能产生不良影响。然后,您需要查看此块各个部分的速度。如果其中一个看上去比其他慢得多,则需要特别注意它以及如何分析它。

调用用于度量时间的方法之间的代码越少,与度量问题状况无关的事物被度量的可能性就越低。

▍考虑不同输入值下功能行为的特征


在实际应用中,在特定功能的输入处接收的数据可能会非常不同。如果测量传递给随机选择的数据集的函数的性能,则不会提供任何有价值的信息来阐明正在发生的事情。

研究性能时需要使用尽可能类似于真实数据的输入数据来调用函数。

functions多次运行功能


假设您有一个遍历数组的函数。她使用数组的每个元素执行一些计算,然后返回一个包含计算结果的新数组。考虑优化此功能时,您想知道什么在您的情况下更有效-循环forEach或常规循环for

这是此功能的两个选项:

function testForEach(x) {
  console.time('test-forEach');
  const res = [];
  x.forEach((value, index) => {
    res.push(value / 1.2 * 0.1);
  });

  console.timeEnd('test-forEach')
  return res;
}

function testFor(x) {
  console.time('test-for');
  const res = [];
  for (let i = 0; i < x.length; i ++) {
    res.push(x[i] / 1.2 * 0.1);
  }

  console.timeEnd('test-for')
  return res;
}

测试功能:

const x = new Array(100000).fill(Math.random());
testForEach(x);
testFor(x);

运行代码后,我们得到以下结果:

test-forEach: 27ms - timer ended
test-for: 3ms - timer ended

周期似乎forEach比周期慢得多for毕竟,测试结果表明了这一点吗?

实际上,经过一次测试,得出这样的结论为时尚早。让我们尝试两次调用函数:

testForEach(x);
testForEach(x);
testFor(x);
testFor(x);

我们得到以下内容:

test-forEach: 13ms - timer ended
test-forEach: 2ms - timer ended
test-for: 1ms - timer ended
test-for: 3ms - timer ended

事实证明,forEach第二次使用它的功能与第二次使用的功能一样快for但是,考虑到第一个forEach函数调用的事实,该函数的运行速度要慢得多,可能仍然不值得使用它。

▍在不同的浏览器中测试性能


以上测试是在Firefox中进行的。但是,如果您在Chrome中执行它们怎么办?结果将完全不同:

test-forEach: 6.156005859375ms
test-forEach: 8.01416015625ms
test-for: 4.371337890625ms
test-for: 4.31298828125ms

事实是,Chrome浏览器和Firefox浏览器基于实现不同性能优化的不同JavaScript引擎。了解这些差异非常有用。

在这种情况下,Firefox可以forEach通过类似的输入获得更好的优化而且周期forforEachChrome和Firefox 都快因此,最好关注函数c的变体for

这是一个很好的例子,展示了在不同浏览器中衡量性能的重要性。如果仅在Chrome中评估某些代码的性能,则可以得出以下结论:与周期forEach相比,周期for还不错。

artificial对系统资源应用人为限制


在我们的实验中获得的值看起来并不特别大。但是请注意,用于开发的计算机通常比浏览网络所用的普通手机要快得多。

为了让自己代替没有最快设备的用户,请使用浏览器的功能来人为地限制系统资源。例如-降低处理器性能。

使用这种方法,10或50毫秒可以轻松变为500毫秒。

relative衡量相对表现


性能测量通常不仅取决于硬件,还取决于当前的处理器负载以及JavaScript应用程序主线程的工作负载。因此,请尝试依靠表征性能变化的相对指标,因为在不同时间分析同一代码片段时获得的绝对指标可能会有很大差异。

摘要


在本文中,我们研究了一些旨在衡量性能的JavaScript API。我们讨论了如何使用它们来分析真实代码。我相信,要执行一些简单的测量,最容易使用console.time()

我感到许多前端开发人员没有对测量项目的性能给予足够的重视。并且他们应该不断监视相关指标,因为生产力会影响项目的成功和盈利。

亲爱的读者们!如果您不断监视项目的性能,请告诉我们您的操作方式。


All Articles