首页 文章

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

提问于
浏览
842

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

我最初在2008年问过这个问题 . 然后,接受的答案是使用新的Date() . getTime()但是,我们现在都同意使用标准的performance.now()API更合适 . 因此,我正在改变对此问题的接受答案 .

20 回答

  • 1254

    process.hrtime()在 Node.js 中可用 - 它返回一个以纳秒为单位的值

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

    谢谢,Achim Koellner,会稍微扩展你的答案:

    var t0 = process.hrtime();
    //Start of code to measure
    
    //End of code
    var timeInMilliseconds = process.hrtime(t0)[1]/1000000; // dividing by 1000000 gives milliseconds from nanoseconds
    

    请注意,除了要测量的内容之外,您不应该做任何事情(例如, console.log 也需要时间来执行并将影响性能测试) .

    注意,按顺序测量异步函数的执行时间,你应该在回调中插入 var timeInMilliseconds = process.hrtime(t0)[1]/1000000; . 例如,

    var t0 = process.hrtime();
    someAsyncFunction(function(err, results) {
    var timeInMilliseconds = process.hrtime(t0)[1]/1000000;
    
    });
    
  • 7

    接受的答案是 wrong

    由于JavaScript是异步的,因此接受的答案的变量结束的值将是错误的 .

    var start = new Date().getTime();
    
    for (i = 0; i < 50000; ++i) {
    // JavaScript is not waiting until the for is finished !!
    }
    
    var end = new Date().getTime();
    var time = end - start;
    alert('Execution time: ' + time);
    

    for的执行可能非常快,因此您无法看到结果是错误的 . 您可以使用代码执行某些请求来测试它:

    var start = new Date().getTime();
    
    for (i = 0; i < 50000; ++i) {
      $.ajax({
        url: 'www.oneOfYourWebsites.com',
        success: function(){
           console.log("success");
        }
      });
    }
    
    var end = new Date().getTime();
    var time = end - start;
    alert('Execution time: ' + time);
    

    因此警报会很快提示,但在控制台中你会看到ajax请求正在继续 .

    这是你应该怎么做:https://developer.mozilla.org/en-US/docs/Web/API/Performance.now

  • 17

    几个月前,我将自己的例程放在一起,使用Date.now()来计算函数 - 即使当时接受的方法似乎是performance.now() - 因为性能对象尚未可用(构建) -in)在稳定的Node.js版本中 .

    今天我正在做更多的研究,并找到了另一种计时方法 . 既然我也发现了如何在Node.js代码中使用它,我想我会在这里分享它 .

    以下是w3cNode.js给出的示例的组合:

    function functionTimer() {
        performance.mark('start')
        functionToBeTimed()
        performance.mark('end')
        performance.measure('Start to End', 'start', 'end')
        const measure = performance.getEntriesByName('Start to End')[0]
        console.log(measure.duration)
    }
    

    NOTE:

    如果您打算在Node.js应用程序中使用 performance 对象,则必须包含以下要求: const { performance } = require('perf_hooks')

  • 10
    export default class Singleton {
    
      static myInstance: Singleton = null;
    
      _timers: any = {};
    
      /**
       * @returns {Singleton}
       */
      static getInstance() {
        if (Singleton.myInstance == null) {
          Singleton.myInstance = new Singleton();
        }
    
        return this.myInstance;
      }
    
      initTime(label: string) {
        this._timers[label] = Date.now();
        return this._timers[label];
      }
    
      endTime(label: string) {
        const endTime = Date.now();
        if (this._timers[label]) {
          const delta = endTime - this._timers[label];
          const finalTime = `${label}: ${delta}ms`;
          delete this._timers[label];
          return finalTime;
        } else {
          return null;
        }
      }
    }
    

    InitTime与 string 有关 .

    return Singleton.getInstance().initTime(label); // Returns the time init

    return Singleton.getInstance().endTime(label); // Returns the total time between init and end

  • -2

    If you need to get function execution time on your local development machine ,您可以使用浏览器的分析工具,也可以使用控制台命令,例如console.time()console.timeEnd() .

    所有现代浏览器都内置了JavaScript分析器 . 这些分析器应该提供最准确的测量,因为您不必修改现有代码,这可能会影响函数的执行时间 .

    要分析您的JavaScript:

    • Chrome 中,按F12并选择“配置文件”选项卡,然后选择“收集JavaScript CPU配置文件” .

    • Firefox 中,安装/打开Firebug,然后单击“配置文件”按钮 .

    • IE 9+ 中,按F12,单击脚本或事件探查器(取决于您的IE版本) .

    Alternatively, on your development machine ,您可以使用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() 的返回值未定义 .

    If you want to get function execution time in the wild ,您必须检测代码 . 你有几个选择 . 您可以通过查询 new Date().getTime() 来简单地保存开始和结束时间:

    function a() {
      var start = new Date().getTime();
      ... do stuff ...
      var end = new Date().getTime();
      var dur = end - start;
    }
    

    但是, Date 对象只有毫秒级的分辨率,并且会受到任何操作系统's system clock changes. In modern browsers, there'的影响 .

    更好的选择是使用High Resolution Time,又名 window.performance.now() . now() 在两个重要方面优于传统的 Date.getTime()

    • now() 是具有亚毫秒分辨率的双精度,表示自页面导航开始以来的毫秒数 . 它返回小数中的微秒数(例如,1000.123的值是1秒和123微秒) .

    • now() 是单调增加的 . 这很重要,因为 Date.getTime() 可能会在后续呼叫中向前跳跃甚至向后跳跃 . 值得注意的是,如果OS的系统时间更新(例如原子钟同步),则 Date.getTime() 也会更新 . now() 保证始终单调增加,因此它不受操作系统系统时间的影响 - 它将始终是挂钟时间(假设你的挂钟不是原子的......) .

    now() 几乎可以在 new Date().getTime()+ new DateDate.now() 所使用的每个地方使用 . 例外是 Datenow() 次不混合,因为 Date 基于unix-epoch(自1970年以来的毫秒数),而 now() 是自页面导航开始以来的毫秒数(因此它将远小于 Date ) .

    以下是如何使用 now() 的示例:

    function a() {
      var start = window.performance.now();
       ... do stuff ...
      var end = window.performance.now();
      var dur = end - start;
    }
    

    Chrome stable,Firefox 15和IE10支持 now() . 还有几个polyfills可用 .

    One other option for measuring execution time in the wild is UserTiming . UserTiming的行为与 console.time()console.timeEnd() 类似,但它使用相同的High分辨率 now() 使用的时间戳(因此您得到一个亚毫秒单调增加的时钟),并将时间戳和持续时间保存到PerformanceTimeline .

    UserTiming具有标记(时间戳)和度量(持续时间)的概念 . 您可以根据需要定义任意数量,并将它们暴露在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可用(我写的) .

  • 582

    由于某些主要浏览器(即IE10)不支持 console.timeperformance.now ,因此我创建了一个利用最佳可用方法的超薄实用程序 . 但是,它缺少错误处理的错误处理(在未初始化的计时器上调用 End() ) .

    使用它并根据需要进行改进 .

    Performance: {
        Timer: {},
        Start: function (name) {
            if (console && console.time) {
                console.time(name);
            } else if (window.performance.now) {
                this.Timer[name] = window.performance.now();
            } else {
                this.Timer[name] = new Date().getTime();
            }
        },
        End: function (name) {
            if (console && console.time) {
                console.timeEnd(name);
            } else {
                var result;
                if (window.performance.now) {
                    result = window.performance.now() - this.Timer[name];
                } else {
                    result = new Date().getTime() - this.Timer[name];
                }
                console.log(name + ": " + result);
            }
        }
    }
    
  • 4

    使用Firebug,启用Console和Javascript . 点击 Profiles 刷新 . 再次单击配置文件查看报告 .

  • -5

    你也可以在这里使用add运算符

    var start = +new Date();
     callYourFunctionHere();
     var end = +new Date();
     var time = end - start;
     console.log('total execution time = '+ time + 'ms');
    
  • 0

    为了进一步扩展vsync的代码以使得能够在NodeJS中返回timeEnd作为值,使用这段代码 .

    console.timeEndValue = function(label) { // Add console.timeEndValue, to add a return value
       var time = this._times[label];
       if (!time) {
         throw new Error('No such label: ' + label);
       }
       var duration = Date.now() - time;
       return duration;
    };
    

    现在使用代码如下:

    console.time('someFunction timer');
    
    someFunction();
    
    var executionTime = console.timeEndValue('someFunction timer');
    console.log("The execution time is " + executionTime);
    

    这为您提供了更多可能性 . 您可以存储执行时间以用于更多目的,例如在方程式中使用它,或存储在数据库中,通过websockets发送到远程客户端,在网页上提供服务等 .

  • 1

    在我的情况下,我更喜欢使用@ grammar suger并用babel编译它 .
    这种方法的问题是函数必须在对象内部 .

    示例JS代码

    function timer() {
        return (target, propertyKey, descriptor) => {
            const start = Date.now();
            let oldFunc = descriptor.value;
    
            descriptor.value = async function (){
                var result = await oldFunc.apply(this, arguments);
                console.log(Date.now() - start);
                return result;
            }
        }
    }
    
    // Util function 
    function delay(timeout) {
        return new Promise((resolve) => setTimeout(() => {
            resolve();
        }, timeout));
    }
    
    class Test {
        @timer()
        async test(timout) {
            await delay(timout)
            console.log("delay 1");
            await delay(timout)
            console.log("delay 2");
        }
    }
    
    const t = new Test();
    t.test(1000)
    t.test(100)
    

    .babelrc(适用于巴贝尔6)

    {
        "plugins": [
            "transform-decorators-legacy"
        ]
     }
    
  • 0
    var StopWatch = function (performance) {
        this.startTime = 0;
        this.stopTime = 0;
        this.running = false;
        this.performance = performance === false ? false : !!window.performance;
    };
    
    StopWatch.prototype.currentTime = function () {
        return this.performance ? window.performance.now() : new Date().getTime();
    };
    
    StopWatch.prototype.start = function () {
        this.startTime = this.currentTime();
        this.running = true;
    };
    
    StopWatch.prototype.stop = function () {
        this.stopTime = this.currentTime();
        this.running = false;
    };
    
    StopWatch.prototype.getElapsedMilliseconds = function () {
        if (this.running) {
            this.stopTime = this.currentTime();
        }
    
        return this.stopTime - this.startTime;
    };
    
    StopWatch.prototype.getElapsedSeconds = function () {
        return this.getElapsedMilliseconds() / 1000;
    };
    
    StopWatch.prototype.printElapsed = function (name) {
        var currentName = name || 'Elapsed:';
    
        console.log(currentName, '[' + this.getElapsedMilliseconds() + 'ms]', '[' + this.getElapsedSeconds() + 's]');
    };
    

    基准

    var stopwatch = new StopWatch();
    stopwatch.start();
    
    for (var index = 0; index < 100; index++) {
        stopwatch.printElapsed('Instance[' + index + ']');
    }
    
    stopwatch.stop();
    
    stopwatch.printElapsed();
    

    产量

    Instance[0] [0ms] [0s]
    Instance[1] [2.999999967869371ms] [0.002999999967869371s]
    Instance[2] [2.999999967869371ms] [0.002999999967869371s]
    /* ... */
    Instance[99] [10.999999998603016ms] [0.010999999998603016s]
    Elapsed: [10.999999998603016ms] [0.010999999998603016s]
    

    performance.now()是可选的 - 只需将false传递给StopWatch构造函数 .

  • 3

    这是计时功能的装饰器

    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;   
    }
    

    用法:

    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'
    

    如果您正在使用异步函数,可以使 timed 异步并在f(... args)之前添加 await ,这应该适用于那些 . 如果您希望一个装饰器同时处理同步和异步功能,它会变得更复杂 .

  • 0

    不要使用Date() . 参见下文 .

    Use 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

    • 歌剧15

    • Android 4.4

    • etc, etc

    console.time 可能是可行的 for you ,但它是非标准的§

    此功能不标准,不符合标准 . 不要在面向Web的 生产环境 站点上使用它:它不适用于每个用户 . 实现之间可能存在很大的不兼容性,并且行为可能在将来发生变化 .

    除了浏览器支持, performance.now 似乎有可能提供更准确的时间,因为它似乎是 console.time 的简单版本 .


    <rant>另外, DON'T EVER use Date for anything 因为它受到"system time"中的变化的影响 . 这意味着当用户没有准确的系统时间时,我们 will 会得到无效的结果 - 如"negative timing":

    2014年10月,我的系统时钟乱了,猜猜是什么....我打开了Gmail,看到我所有的一天的电子邮件“在0分钟前发送” . 我认为Gmail应该由Google的世界级工程师构建.......

    (将您的系统时钟设置为一年前,然后转到Gmail,这样我们就可以大笑 . 也许有一天我们会为JS Date 提供Hall of Shame . )

    Google Spreadsheet的now()功能也遇到了这个问题 .

    您将使用 Date 的唯一时间是您想要显示用户 his 系统时钟时间 . 不是当你想得到the time或测量任何东西 .

  • 47

    它可能会帮助你 .

    var t0 = date.now(); doSomething(); var t1 = date.now(); console.log("Call to doSomething took approximate" + (t1 - t0)/1000 + " seconds.")

  • 28

    如果要测量多个未嵌套的东西之间的时间,可以使用:

    function timer(lap){ 
        if(lap) console.log(`${lap} in: ${(performance.now()-timer.prev).toFixed(3)}ms`); 
        timer.prev = performance.now();
    }
    

    与console.time()类似,但如果您不需要跟踪以前的计时器,则更容易使用 .

    如果您喜欢console.time()中的蓝色,则可以使用此行代替

    console.log(`${lap} in: %c${(performance.now()-timer.prev).toFixed(3)}ms`, 'color:blue');
    
    // Usage: 
    timer()              // set the start
    // do something 
    timer('built')       // logs 'built in: 591.815ms'
    // do something
    timer('copied')      // logs 'copied in: 0.065ms'
    // do something
    timer('compared')    // logs 'compared in: 36.41ms'
    
  • 5

    如前所述检查并使用内置计时器 . 但如果你想要或者需要自己编写,那就是我的两分钱:

    //=-=|Source|=-=//
    /**
     * JavaScript Timer Object
     *
     *      var now=timer['elapsed'](); 
     *      timer['stop']();
     *      timer['start']();
     *      timer['reset']();
     * 
     * @expose
     * @method timer
     * @return {number}
     */
    timer=function(){
        var a=Date.now();
        b=0;
        return{
            /** @expose */
            elapsed:function(){return b=Date.now()-a},
            start:function(){return a=Date.now()},
            stop:function(){return Date.now()},
            reset:function(){return a=0}
        }
    }();
    
    //=-=|Google Advanced Optimized|=-=//
    timer=function(){var a=Date.now();b=0;return{a:function(){return b=Date.now()-a},start:function(){return a=Date.now()},stop:function(){return Date.now()},reset:function(){return a=0}}}();
    

    编译成功!

    • 原始大小:219字节gzip(405字节未压缩)

    • 编译大小:gzip压缩109字节(未压缩187字节)

    • 从gzip大小中节省了50.23%(53.83%没有gzip

  • 382

    使用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);
    
  • 1

    要获得精确值,您应该使用Performance interface . 它's supported in modern versions of Firefox, Chrome, Opera and IE. Here'是一个如何使用它的例子:

    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中测量执行时间,请查看这个辉煌的post . 作者还提供了几个链接关于JavaScript时间的准确性,值得一读 .

  • 3

    使用performance.now():

    var t0 = performance.now();
    
    doSomething();   // <---- The function you're measuring time for 
    
    var t1 = performance.now();
    console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")
    

    NodeJs:需要导入性能类


    使用console.time :(非标准)(生活标准)

    console.time('someFunction');
    
    someFunction(); // Whatever is timed goes between the two "console.time"
    
    console.timeEnd('someFunction');
    

    Note
    传递给 time()timeEnd() 方法的字符串必须匹配
    (计时器按预期完成) .

    console.time()文档:有关MDN(客户端)文档的NodeJS文档

相关问题