如何测量函数执行所需的时间

1722

我需要以毫秒为单位获得执行时间。

我最初在2008年提出了这个问题。当时被接受的答案是使用 new Date().getTime()。然而,现在我们都同意使用标准的performance.now() API更加合适。因此,我将接受的答案更改为这个答案。


5
通常,说明您在执行时间方面想要实现的目标可能比仅回答问题更有用。现在,使用Firebug或Chrome Dev工具中的Profiling通常是查找占用CPU资源的代码的更好方法。 - oligofren
16
performance.now() 在 Node 中无法使用。你可以使用 new Date().getTime() 来代替,在 Node 中它能正常工作。 - Ryan Walker
2
@RyanWalker或者更简单的Date.now(),在node中也可以工作。 - f278f1b2
2
@oligofren - 有时候你可能想要捕获这些数据。我有一个情况,我正在将它写入indexedDB。 - ThomasRones
1
我想知道是否有最新的文档(例如在2020/201)表明在Node中performance.now()比Date.now()更好? - Qiulang
对于一个可以用于函数的一行代码,使它们在每次运行时记录其执行时间,请查看下面的答案 https://dev59.com/3XRC5IYBdhLWcg3wYf8p#52286301。它将计时逻辑封装到一个装饰器中,以便根据需要将其“包装”到函数中。 - aljgom
30个回答

2693

使用performance.now():

var startTime = performance.now()

doSomething()   // <---- measured code goes between startTime and endTime
    
var endTime = performance.now()

console.log(`Call to doSomething took ${endTime - startTime} milliseconds`)

Node.js 中,需要导入 performance 类。

导入性能类。

const { performance } = require('perf_hooks');

使用console.time:(最新标准

console.time('doSomething')
    
doSomething()   // <---- The function you're measuring time for 
    
console.timeEnd('doSomething')

注意:
传递给 time()timeEnd() 方法的字符串必须匹配
以便计时器如预期般结束)。

console.time() 文档:


36
现在它也得到了Chrome开发者工具的支持。 - julien_c
4
据我所知,目前这是收集准确时间的最佳方法。 - Ash Blue
8
那两个语句之间难道不需要执行函数吗?现在你测量的是定义函数的时间,而不是执行函数的时间。如果我说错了请纠正我。 - Cristian
7
是的,你可以使用 totalTime += console.timeEnd('timer'),并在每个计时器上都这样做。 - vsync
3
这是performance.now()的当前状态:时间戳实际上不是高分辨率的。为了减轻像Spectre这样的安全威胁,浏览器目前会对结果进行不同程度的四舍五入(Firefox从Firefox 59开始对结果进行2毫秒的四舍五入)。有些浏览器可能还会略微随机化时间戳。精度可能会在将来的版本中再次提高;浏览器开发人员仍在研究这些定时攻击以及如何最好地减轻它们。 - miknik
显示剩余24条评论

707

使用 new Date().getTime() 方法。

getTime() 方法返回自 1970 年 1 月 1 日午夜以来的毫秒数。

例如:

var start = new Date().getTime();

for (i = 0; i < 50000; ++i) {
// do something
}

var end = new Date().getTime();
var time = end - start;
alert('Execution time: ' + time);

10
注意,您可以将+new Date()替换为getTime()调用: var start = +new Date(); // 做一些事情 alert("执行时间:"+(+new Date())-start); - J c
65
时间不准确,因为日期不适用于这种功能。在此我要大胆地说,如果您想要精确的时间控制,请使用vsync的示例。尽管目前它只在Chrome和Firefox中运行。 - Ash Blue
10
注意,getMilliseconds() 方法会返回当前秒钟的毫秒数。如果你用 getMilliseconds() 替换 getTime(),当你跨越秒钟边界时,可能会得到负数的结果。 - RickyA
7
Vsync的答案在今天的标准下更加正确,使用Date()可能会导致非常错误的结果显示,特别是在Windows平台上,结果可能会被舍入和截断到最近的15毫秒边界,导致奇怪的东西,如微小代码片段的0毫秒计时。 - oligofren
30
@AshBlue,我们应该使用 window.performance.now。请参考 https://dev59.com/3XRC5IYBdhLWcg3wYf8p#15641427。 - Pacerier
显示剩余9条评论

459

不要使用Date()。请阅读以下内容。

使用performance.now()

<script>
var a = performance.now();
alert('do something...');
var b = performance.now();
alert('It took ' + (b - a) + ' ms.');
</script>

适用于以下浏览器:

  • IE 10 ++

  • FireFox 15 ++

  • Chrome 24 ++

  • Safari 8 ++

  • Opera 15 ++

  • Android 4.4 ++

  • 等等

console.time 可能 适合你使用,但它是非标准的§

此功能是非标准的,不在标准轨道上。不要在面向 Web 的生产站点上使用它:它不会对每个用户都起作用。实现之间可能也存在大的不兼容性,并且行为可能在未来发生变化。

除了浏览器支持外,performance.now似乎有潜力提供更准确的时间,因为它似乎是console.time的简化版本。请注意,此处的“potential”表示潜在的可能性。

<rant> 另外,千万不要在任何情况下使用Date,因为它受到“系统时间”的影响。这意味着当用户的系统时间不准确时,我们将会得到无效的结果,例如“负数计时”:

2014年10月,我的系统时钟出了问题,你猜怎么着……我打开Gmail看到所有当天的邮件显示“发送于0分钟前”。而我曾经以为Gmail是由Google的世界级工程师构建的……

(把你的系统时钟调到一年前,然后去Gmail看看,我们都可以开怀大笑。也许有一天我们会为JS Date设立Hall of Shame。)

Google电子表格的now()函数也存在这个问题。

你只有在想要显示用户系统时钟时间时才会使用Date。而不是当你想获得the time或测量任何东西时。

4
正是我需要的!我希望能够将几个时间相加,使用控制台无法实现。 - Ray
10
请注意,目前Safari浏览器不支持此功能:https://developer.mozilla.org/zh-CN/docs/Web/API/Performance/now() - Akos K
3
我使用Firebug Profile和performance.now(),它们都运行良好。Performance.now() 证实了我的Profile结果。 - Vincent Jia
3
我的最大问题是它在IE7上无法运行(公司客户使用)。我不关心在Chrome中测量性能,因为它总是非常快。 - Nick
3
这是一种比console.time()更好的方法。 - Sanjeev
显示剩余4条评论

65

如果你需要在本地开发机器上获取函数执行时间,你可以使用浏览器的性能分析工具或控制台命令,例如console.time()console.timeEnd()

所有现代浏览器都内置了JavaScript分析工具。这些分析工具应该会给出最准确的测量结果,因为您不需要修改现有的代码,这可能会影响函数的执行时间。

要对JavaScript进行分析:

  • Chrome 中,请按 F12 并选择 Profiles 选项卡,然后选择 Collect JavaScript CPU Profile
  • Firefox 中,安装/打开Firebug,并单击 Profile 按钮。
  • IE 9+ 中,请按 F12,单击 ScriptProfiler(根据您的IE版本而定)。

或者,在您的开发机器上,您可以使用 console.time()console.timeEnd() 为代码添加仪器。这些函数在Firefox11+、Chrome2+和IE11+中受支持,报告您通过 console.time() 启动/停止的计时器。 time() 接受一个用户定义的计时器名称作为参数,timeEnd() 然后报告自计时器启动以来的执行时间:

function a() {
  console.time("mytimer");
  ... do stuff ...
  var dur = console.timeEnd("myTimer"); // NOTE: dur only works in FF
}

请注意,只有Firefox在timeEnd()调用中返回经过的时间。其他浏览器只是将结果报告给开发者控制台:调用timeEnd()的返回值为未定义。

如果您想在实际应用中获取函数执行时间,您需要在代码中添加工具。您有几个选项。您可以通过查询new Date().getTime()来简单保存开始和结束时间:

function a() {
  var start = new Date().getTime();
  ... do stuff ...
  var end = new Date().getTime();
  var dur = end - start;
}
然而,Date对象只有毫秒级的分辨率,并且会受到任何操作系统系统时钟更改的影响。在现代浏览器中,有一个更好的选择。
更好的选择是使用高分辨率时间,也称为window.performance.now()now()比传统的Date.getTime()更好,有两个重要的优点:
  1. now()是一个双精度数字,具有亚毫秒的分辨率,表示自页面导航开始以来的毫秒数。它返回小数部分中的微秒数(例如,值为1000.123是1秒和123微秒)。
  2. now()是单调递增的。这很重要,因为Date.getTime()可能在后续调用中可能会向前或向后跳跃。值得注意的是,如果更新了操作系统的系统时间(例如,原子钟同步),则Date.getTime()也会被更新。now()保证始终单调递增,因此不受操作系统的系统时间影响--它始终是挂钟时间(假设您的挂钟不是原子钟...)。
now()可以用于几乎所有需要new Date().getTime()+ new DateDate.now()的地方。例外情况是Datenow()时间不能混合使用,因为Date基于Unix纪元(自1970年以来的毫秒数),而now()是自页面导航开始以来的毫秒数(因此它比Date小得多)。
下面是如何使用now()的示例:
function a() {
  var start = window.performance.now();
   ... do stuff ...
  var end = window.performance.now();
  var dur = end - start;
}

now() 在Chrome stable、Firefox 15+和IE10中得到支持。此外,还有几个polyfills可用。

在实际环境中测量执行时间的另一种选择是UserTiming。UserTiming的行为类似于console.time()console.timeEnd(),但它利用与now()相同的高分辨率时间戳(因此获得亚毫秒单调递增时钟),并将时间戳和持续时间保存到PerformanceTimeline中。

UserTiming具有marks(时间戳)和measures(持续时间)的概念。您可以定义任意数量的任何一个,并且它们都会暴露在PerformanceTimeline上。

要保存时间戳,请调用mark(startMarkName)。要获取自第一个标记以来的持续时间,只需调用measure(measurename, startMarkname)即可。然后,持续时间将与您的标记一起保存在PerformanceTimeline中。

function a() {
  window.performance.mark("start");
  ... do stuff ...
  window.performance.measure("myfunctionduration", "start");
}

// duration is window.performance.getEntriesByName("myfunctionduration", "measure")[0];

UserTiming在IE10+和Chrome25+中可用。此外还有一个polyfill(由我编写)可用。


1
在我看来,这是一个非常好且最新的答案 :) 如果稍微编辑一下会更好。我认为当基准测试不是在开发机器本身上完成时,用户计时不是“另一种选择”,而是首选选择。使用您的polyfill可以在所有浏览器上运行。隐藏performance.nowDate的细节和样板是它存在的原因。 - hashchange

39

为了获得精确的值,您应该使用性能接口。它受现代版本的Firefox、Chrome、Opera和IE支持。下面是一个示例,说明如何使用它:

var performance = window.performance;
var t0 = performance.now();
doWork();
var t1 = performance.now();
console.log("Call to doWork took " + (t1 - t0) + " milliseconds.")

Date.getTime()console.time()并不适合精确测量执行时间。如果可以接受快速的粗略估计,则可以使用它们。我的意思是说,你可能会比实际时间多出15-60毫秒。

请查看这篇关于JavaScript中测量执行时间的精彩文章。作者还提供了一些有关JavaScript时间准确性的链接,值得一读。


这种方法最好,因为被接受的答案需要 node.js,而这个答案不需要。巧合的是,你的总积分 1,963 等于我的出生年份! - WinEunuuchs2Unix
@WinEunuuchs2Unix 啊,你的总积分 1,382 对我来说并没有什么特别的意义。达到 1,991 分,给我五个赞 :) - Varvara Kalinina

21
一个简单的解决方案,你也可以在这里使用加法运算符。
 var start = +new Date();
 callYourFunctionHere();
 var end = +new Date();
 var time = end - start;
 console.log('total execution time = '+ time + 'ms');

1
谢谢,但是+new Date()中的+是什么?你是在强制转换为数字吗? - Bardelman
2
@Bardelman 没错,它和 Number(new Date()) 是一样的。 - Alok Deshwal

21

使用 Firebug,启用控制台和 JavaScript。点击“分析”。重新加载页面。再次点击“分析”。查看报告。


9
好的建议,但显然只适用于FF。我们经常想比较浏览器速度... :-) - PhiLho
4
在新版本的Firebug中,他们将这些选项隐藏在菜单中,请使用CTRL + SHIFT + P或console.profile(); console..profileEnd()。 - user956584
6
Chrome现在也支持console.time()console.timeEnd() - julien_c

16

Node.js 中可用 process.hrtime() 方法 - 它返回纳秒级别的值。

let hrTime = process.hrtime()
console.log(hrTime[0] * 1000000 + hrTime[1] / 1000)

1
如果你更喜欢使用毫秒(ms)而不是建议的微秒(μs),那么以下代码可以将高分辨率时间转换为毫秒:hrtime[0] * 1000 + hrtime[1] / 1000000。哈哈,我也更喜欢使用 var hrtime!:P - cregox

14

这是一个用于计时函数的修饰符。

它将函数包装起来,每次运行时都会计时。

使用方法:

let test = () => { /* does something */ }
test = timed(test)   // turns the function into a timed function in one line
test()               // run your code as normal, logs 'function test took 1001.900ms' 

这是装饰器:

let timed = (f) => (...args) => {
    let start = performance.now();
    let ret = f(...args);
    console.log(`function ${f.name} took ${(performance.now() - start).toFixed(3)}ms`);
    return ret;   
}
如果您正在使用异步函数,您可以使 timed 成为异步函数,并在 f(...args) 前添加 await,这样就可以正常运行了。如果要让一个装饰器处理同步和异步函数,则会变得更加复杂。

1
这正是我正在寻找的。谢谢! - Andrew Watters
有没有办法让它通用地与异步函数一起使用? - TotalAMD
1
这很棒,有没有办法让它遵循 TypeScript 类型? - Tim Trewartha

14

可以仅使用一个变量:

var timer = -performance.now();

// Do something

timer += performance.now();
console.log("Time: " + (timer/1000).toFixed(5) + " sec.")

timer/1000 - 将毫秒转换为秒

.toFixed(5) - 保留小数点后五位


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