关于javascript:如何测量函数执行所花费的时间

How to measure time taken by a function to execute

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

I originally asked this question back in 2008. The accepted answer
then was to use new Date().getTime() However, we can all agree now
that using the standard performance.now() API is more
appropriate. I am therefore changing the accepted answer to this one.


使用性能。now():

1
2
3
4
5
6
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: it is required to import the performance class

使用console.time:(non-standard)(live standard)

1
2
3
4
5
console.time('someFunction');

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

console.timeEnd('someFunction');

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

console.time() documentations:

  • NodeJS documentation regarding
  • MDN (client-side) documentation

  • 使用new date().gettime()。

    The getTime() method returns the number of milliseconds since midnight of January 1, 1970.

    前任。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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);


    不要使用日期()。在下面阅读。

    使用performance.now()

    1
    2
    3
    4
    var a = performance.now();
    alert('do something...');
    var b = performance.now();
    alert('It took ' + (b - a) + ' ms.');

    它的作用是:

    • IE 10 ++

    • 火狐15 ++

    • 铬24+

    • Safari 8 ++

    • 歌剧15 +

    • Android 4.4 +

    EDOCX1[3]可能对您是可行的,但它不是标准的§;:

    This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.

    除了浏览器支持之外,performance.now似乎还有潜力提供更精确的时间安排,因为它似乎是console.time的基本版本。

    另外,不要将Date用于任何事情,因为它受"系统时间"变化的影响。这意味着,当用户没有准确的系统时间时,我们将得到无效的结果,如"负计时"。

    On Oct 2014, my system clock went haywire and guess what.... I opened Gmail and saw all of my day's emails"sent 0 minutes ago". And I'd thought Gmail is supposed to be built by world-class engineers from Google.......

    (将您的系统时钟设置为一年前,然后转到Gmail,这样我们就可以好好地笑了。也许有一天,我们会为js Date建立一个耻辱堂。)

    谷歌电子表格的now()功能也面临着这个问题。

    您使用Date的唯一时间是想向用户显示系统时钟时间。当你想得到时间或者测量任何东西的时候就不要。


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

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

    要分析您的javascript:

    • 在Chrome中,按F12并选择配置文件选项卡,然后收集javascript CPU配置文件。
    • 在firefox中,安装/打开firebug,然后单击profile按钮。
    • 在IE 9+中,按F12,单击脚本或探查器(取决于您的IE版本)。

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

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

    请注意,只有firefox返回timeEnd()调用所用的时间。其他浏览器只是将结果报告给开发人员控制台:timeEnd()的返回值未定义。

    如果您想在野外获得函数执行时间,则必须插入代码。你有几个选择。您只需查询new Date().getTime()即可保存开始和结束时间:

    1
    2
    3
    4
    5
    6
    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()

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

  • now()是单调递增的。这一点很重要,因为Date.getTime()可能在随后的调用中向前甚至向后跳。值得注意的是,如果操作系统的系统时间被更新(例如原子钟同步),那么Date.getTime()也会被更新。now()被保证总是单调地递增,所以它不受操作系统的系统时间的影响--它将永远是壁钟时间(假设您的壁钟不是原子钟…)。

  • now()几乎可以在new Date().getTime()+ new DateDate.now()所在的所有地方使用。例外情况是Datenow()时间不混合,因为Date是基于unix epoch(1970年以来的毫秒数),而now()是自页面导航开始以来的毫秒数(因此它将比Date小得多)。

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

    1
    2
    3
    4
    5
    6
    function a() {
      var start = window.performance.now();
       ... do stuff ...
      var end = window.performance.now();
      var dur = end - start;
    }

    now()支持Chrome稳定版、Firefox 15+和IE10。还有几种可用的多边形填充。

    在野外测量执行时间的另一个选项是用户计时。用户计时与console.time()console.timeEnd()的行为类似,但它使用了now()使用的相同的高分辨率时间戳(因此您得到一个亚毫秒单调递增的时钟),并将时间戳和持续时间保存到performancetimeline。

    用户计时具有标记(时间戳)和度量(持续时间)的概念。您可以根据自己的需要定义任意一个,它们都会在PerformanceTimeline上公开。

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

    1
    2
    3
    4
    5
    6
    7
    function a() {
      window.performance.mark("start");
      ... do stuff ...
      window.performance.measure("myfunctionduration","start");
    }

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

    用户计时在IE10+和Chrome25+中可用。还有一个polyfill可用(我写的)。


    为了得到精确的值,应该使用性能接口。它在火狐、Chrome、Opera和IE的现代版本中得到了支持。下面是一个如何使用它的示例:

    1
    2
    3
    4
    5
    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时间准确性的链接,值得一读。


    使用firebug,同时启用控制台和javascript。单击配置文件。重新装入。再次单击配置文件。查看报告。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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]');
    };

    基准

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var stopwatch = new StopWatch();
    stopwatch.start();

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

    stopwatch.stop();

    stopwatch.printElapsed();

    产量

    1
    2
    3
    4
    5
    6
    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传递到秒表构造函数函数中。


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

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


    为了进一步扩展vsync的代码,使其能够在nodejs中以值的形式返回timeend,可以使用这段代码。

    1
    2
    3
    4
    5
    6
    7
    8
    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;
    };

    现在使用如下代码:

    1
    2
    3
    4
    5
    6
    console.time('someFunction timer');

    someFunction();

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

    这给了你更多的可能性。您可以将执行时间存储起来,以用于更多目的,例如将其用于公式中,或者存储在数据库中,通过WebSockets发送到远程客户机,在网页上提供服务等。


    您也可以在这里使用添加运算符

    1
    2
    3
    4
    5
     var start = +new Date();
     callYourFunctionHere();
     var end = +new Date();
     var time = end - start;
     console.log('total execution time = '+ time + 'ms');

    只能使用一个变量:

    1
    2
    3
    4
    5
    6
    var timer = -performance.now();

    // Do something

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

    timer/1000—将毫秒转换为秒

    .toFixed(5)—用于修剪额外的数字


    这可能对你有帮助。

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


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

    使用它并根据您的需要加以改进。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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);
            }
        }
    }

    谢谢,Achim Koellner,会扩大你的答案:

    1
    2
    3
    4
    5
    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;。例如,

    1
    2
    3
    4
    5
    var t0 = process.hrtime();
    someAsyncFunction(function(err, results) {
    var timeInMilliseconds = process.hrtime(t0)[1]/1000000;

    });


    几个月前,我用date.now()编写了自己的例程,该例程对一个函数进行了多次运算,尽管当时接受的方法似乎是性能方法。now()。--因为性能对象在stable node.js版本中尚不可用(内置)。

    今天我做了更多的研究,找到了另一种计时方法。因为我在node.js代码中也发现了如何使用它,所以我想在这里共享它。

    下面结合W3C和node.js给出的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    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)
    }

    注:

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


    这是计时功能的装饰器

    1
    2
    3
    4
    5
    6
    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;  
    }

    用途:

    1
    2
    3
    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,这对那些函数是有效的。如果您希望一个装饰器同时处理同步和异步函数,则会变得更复杂。


    如果要测量多个非嵌套对象之间的时间间隔,可以使用以下方法:

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

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

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

    1
    console.log(`${lap} in: %c${(performance.now()-timer.prev).toFixed(3)}ms`, 'color:blue');
    1
    2
    3
    4
    5
    6
    7
    8
    // 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'

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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;
        }
      }
    }

    string相关的初始时间。

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

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


    在我的例子中,我更喜欢使用@grammar-suger并用babel编译它。这个方法的问题是函数必须在对象内部。

    样本JS代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    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(用于babel 6)

    1
    2
    3
    4
    5
     {
       "plugins": [
           "transform-decorators-legacy"
        ]
     }


    具有累积周期的秒表

    使用服务器和客户机(节点或DOM),使用PerformanceAPI。很好,当您有许多小的循环时,例如在一个处理1000个数据对象的1000次函数中,但是您希望看到这个函数中的每个操作是如何合计的。

    所以这一个使用模块全局(单例)计时器。与类的singleton模式相同,只是使用起来简单一点,但是您需要将它放在一个单独的文件中,例如stopwatch.js文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    const perf = typeof performance !=="undefined" ? performance : require('perf_hooks').performance;
    const DIGITS = 2;

    let _timers = {};

    const _log = (label, delta?) => {
        if (_timers[label]) {
            console.log(`${label}: ` + (delta ? `${delta.toFixed(DIGITS)} ms last, ` : '') +
                `${_timers[label].total.toFixed(DIGITS)} ms total, ${_timers[label].cycles} cycles`);
        }
    };

    export const Stopwatch = {
        start(label) {
            const now = perf.now();
            if (_timers[label]) {
                if (!_timers[label].started) {
                    _timers[label].started = now;
                }
            } else {
                _timers[label] = {
                    started: now,
                    total: 0,
                    cycles: 0
                };
            }
        },
        /** Returns total elapsed milliseconds, or null if stopwatch doesn't exist. */
        stop(label, log = false) {
            const now = perf.now();
            if (_timers[label]) {
                let delta;
                if(_timers[label].started) {
                    delta = now - _timers[label].started;
                    _timers[label].started = null;
                    _timers[label].total += delta;
                    _timers[label].cycles++;
                }
                log && _log(label, delta);
                return _timers[label].total;
            } else {
                return null;
            }
        },
        /** Logs total time */
        log: _log,
        delete(label) {
            delete _timers[label];
        }
    };

    实现这一目标有多种方法:

  • 使用console.time

    console.time('function');
    //run the function in between these two lines for that you need to measure time taken
    //by the function. ("ex. function();")
    console.timeEnd('function');

  • 这是最有效的方法:使用性能。现在()

  • var v1 = performance.now();
    //run the function here for which you have top measure the time
    var v2 = performance.now();
    console.log("total time taken ="+(v2-v1)+"milliseconds";

  • 使用+(添加运算符)或GetTime()。

    var h2 = +new Date(); //or
    var h2 = new Date().getTime();
    for(i=0;i<500;i++) {//do something} var h3 = +new Date(); //or var h3 = new Date().getTime(); var timeTaken = h3-h2; console.log("time ====", timeTaken);

  • 下面是将一元加号运算符应用于日期实例时发生的情况:获取相关日期实例的值将其转换为数字

    注意:getTime()比一元+运算符提供更好的性能。


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //=-=|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字节未压缩)
    • 编译大小:109字节gzip(187字节未压缩)
    • 节省了50.23%的gzip大小(53.83%没有gzip

    接受的答案是错误的!

    因为javascript是异步的,所以接受答案的变量end的值是错误的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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的执行速度可能非常快,因此您看不到结果是错误的。您可以使用代码进行测试,并执行一些请求:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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