Как измерить время выполнения кода JavaScript с помощью обратных вызовов?


320

У меня есть фрагмент кода JavaScript, который я выполняю с помощью node.jsинтерпретатора.

for(var i = 1; i < LIMIT; i++) {
  var user = {
    id: i,
    name: "MongoUser [" + i + "]"
  };
  db.users.save(user, function(err, saved) {
    if(err || !saved) {
      console.log("Error");
    } else {
      console.log("Saved");
    }
  });
}

Как я могу измерить время, затраченное на эти операции вставки базы данных? Я мог бы вычислить разницу значений даты после и до этого фрагмента кода, но это было бы неправильно из-за асинхронного характера кода.


8
Просто прочитайте время начала до вызова БД и время окончания ВНУТРИ обратного вызова ..
BFil

Существует вероятность того, что время, когда DB завершает вставку, и время выполнения обратного вызова не совпадают, и это может привести к ошибке в измерении?
Stormshadow

1
Нет, вы не должны беспокоиться об этом, если код библиотеки db хорошо спроектирован и не обрабатывает никакие другие операции до запуска обратного вызова, вы должны принять правильные меры. Вы также можете профилировать вставку, поместив метки времени в код библиотеки, где фактически выполняется вставка, вместо вашей собственной, но, опять же, я бы не стал беспокоиться об этом
BFil

Я бы порекомендовал попробовать NodeTime, который, кажется, хорошо подходит для того, что вы пытаетесь сделать.
Джулиан Найт

Я написал, timerlogчто похоже на, console.time()но с дополнительными функциями; github.com/brillout/timerlog
Brillout

Ответы:


720

Используйте Node.js console.time()и console.timeEnd():

var i;
console.time("dbsave");

for(i = 1; i < LIMIT; i++){
    db.users.save({id : i, name : "MongoUser [" + i + "]"}, end);
}

end = function(err, saved) {
    console.log(( err || !saved )?"Error":"Saved");
    if(--i === 1){console.timeEnd("dbsave");}
};

31
Чистое и встроенное решение для узла.
Бехлюл Учар

45
> Я хочу знать, как измерить время, затрачиваемое на эти операции вставки БД. --- console.timeEnd ("dbsave") просто выводит данные для согласования времени. Вы не можете использовать это дальше и менее гибко. Если вам нужно фактическое значение времени, как в оригинальном вопросе, вы не можете использовать console.timeEnd ("dbsave")
gogaman

@gogaman это хороший момент, так как вы не можете захватить вывод из console.timeEnd (). Возможно, было бы полезно направить вывод в файл и использовать его оттуда?
Дуг Молинье

5
Так в чем же разница между console.time () и process.hrtime () в ответе ниже?
желто-святого

3
Стоит добавить примечание, что время выполнения затем распечатывается, просто чтобы новые пользователи сейчас.
Янко-м

208

Для этого есть метод. Проверьте process.hrtime (); ,

Итак, я в основном положил это в верхней части моего приложения.

var start = process.hrtime();

var elapsed_time = function(note){
    var precision = 3; // 3 decimal places
    var elapsed = process.hrtime(start)[1] / 1000000; // divide by a million to get nano to milli
    console.log(process.hrtime(start)[0] + " s, " + elapsed.toFixed(precision) + " ms - " + note); // print message + time
    start = process.hrtime(); // reset the timer
}

Затем я использую его, чтобы узнать, сколько времени занимают функции. Вот базовый пример, который печатает содержимое текстового файла с именем «output.txt»:

var debug = true;
http.createServer(function(request, response) {

    if(debug) console.log("----------------------------------");
    if(debug) elapsed_time("recieved request");

    var send_html = function(err, contents) {
        if(debug) elapsed_time("start send_html()");
        response.writeHead(200, {'Content-Type': 'text/html' } );
        response.end(contents);
        if(debug) elapsed_time("end send_html()");
    }

    if(debug) elapsed_time("start readFile()");
    fs.readFile('output.txt', send_html);
    if(debug) elapsed_time("end readFile()");

}).listen(8080);

Вот быстрый тест, который вы можете запустить в терминале (оболочка BASH):

for i in {1..100}; do echo $i; curl http://localhost:8080/; done

3
это что превосходит console.time решение в любом случае?
scravy

31
Да, это намного точнее, и вы можете сохранить результат в переменной
Даллас Кларк

Этот работает для меня, так как я хотел вызвать таймер несколько раз
tbh__

2
Почему ты звонишь process.hrtime(start)дважды? Есть ли для этого особая причина?
Сохаил Си

1
process.hrtime ([time]), где time - необязательный параметр, который должен быть результатом предыдущего вызова process.hrtime () для сравнения с текущим временем. Это дает разницу между текущим вызовом и предыдущим вызовом hrtime.
Нилеш Джайн

72

При вызове console.time('label')будет записываться текущее время в миллисекундах, затем при последующем вызове console.timeEnd('label')будет отображаться продолжительность с этой точки.

Время в миллисекундах будет автоматически напечатано рядом с меткой, поэтому вам не нужно делать отдельный вызов console.log для печати метки:

console.time('test');
//some code
console.timeEnd('test'); //Prints something like that-> test: 11374.004ms

Для получения дополнительной информации см . Документы разработчика Mozillaconsole.time .


Что это добавляет к принятому ответу ?
Дан Даскалеску

1
Принятый ответ был изменен после моего ответа, чтобы использовать мой код
jfcorugedo

24

Удивило, что никто не упомянул новые встроенные библиотеки:

Доступно в Node> = 8.5 и должно быть в Modern Browers

https://developer.mozilla.org/en-US/docs/Web/API/Performance

https://nodejs.org/docs/latest-v8.x/api/perf_hooks.html#

Узел 8.5 ~ 9.x (Firefox, Chrome)

// const { performance } = require('perf_hooks'); // enable for node
const delay = time => new Promise(res=>setTimeout(res,time))
async function doSomeLongRunningProcess(){
  await delay(1000);
}
performance.mark('A');
(async ()=>{
  await doSomeLongRunningProcess();
  performance.mark('B');
  performance.measure('A to B', 'A', 'B');
  const measure = performance.getEntriesByName('A to B')[0];
  // firefox appears to only show second precision.
  console.log(measure.duration);
  performance.clearMeasures(); // apparently you should remove entries...
  // Prints the number of milliseconds between Mark 'A' and Mark 'B'
})();

https://repl.it/@CodyGeisler/NodeJsPerformanceHooks

Узел 10.х

https://nodejs.org/docs/latest-v10.x/api/perf_hooks.html

const { PerformanceObserver, performance } = require('perf_hooks');
const delay = time => new Promise(res => setTimeout(res, time))
async function doSomeLongRunningProcess() {
    await delay(1000);
}
const obs = new PerformanceObserver((items) => {
    console.log('PerformanceObserver A to B',items.getEntries()[0].duration);
    performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });

performance.mark('A');

(async function main(){
    try{
        await performance.timerify(doSomeLongRunningProcess)();
        performance.mark('B');
        performance.measure('A to B', 'A', 'B');
    }catch(e){
        console.log('main() error',e);
    }
})();

1
Дает мне TypeError: performance.getEntriesByName is not a functionв Node v10.4.1
Джереми Тилле

Я сделал пример, чтобы вы могли запустить его онлайн. Это Узел 9.7.1. Если это не работает в v10.4.1, то мне интересно, что может измениться!
Коди G

1
Stability: 1 - Experimentalможет быть? :) nodejs.org/docs/latest-v8.x/api/…
Джереми Тилле

Да, это точно изменилось. В версии 10 появился новый наблюдатель, с документами можно ознакомиться по адресу nodejs.org/docs/latest-v10.x/api/documentation.html . Я обновлю, когда у меня будет шанс!
Коди G

19

Для тех, кто хочет получить значение прошедшего времени вместо вывода на консоль:

используйте process.hrtime () в качестве предложения @DDeriso, ниже мой более простой подход:

function functionToBeMeasured() {
    var startTime = process.hrtime();
    // do some task...
    // ......
    var elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime));
    console.log('It takes ' + elapsedSeconds + 'seconds');
}

function parseHrtimeToSeconds(hrtime) {
    var seconds = (hrtime[0] + (hrtime[1] / 1e9)).toFixed(3);
    return seconds;
}

16
var start = +new Date();
var counter = 0;
for(var i = 1; i < LIMIT; i++){
    ++counter;
    db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
          if( err || !saved ) console.log("Error");
          else console.log("Saved");
          if (--counter === 0) 
          {
              var end = +new Date();
              console.log("all users saved in " + (end-start) + " milliseconds");
          }
    });
}

5
Мне пришлось искать синтаксис «+ new Date ()», чтобы понять, что это значит. Согласно комментариям к этому ответу ( stackoverflow.com/a/221565/5114 ), не рекомендуется использовать эту форму из соображений производительности и читабельности. Я предпочитаю что-то более многословное, чтобы читателю было понятнее. Также смотрите этот ответ: stackoverflow.com/a/5036460/5114
Mnebuerquo

3
Я часто использую, var start = process.hrtime(); ... var end = process.hrtime(start);чтобы получить время с высоким разрешением (если мне нужно ожидать точность до миллисекунды)
Андрей Сидоров

9

Старый вопрос, но для простого API и облегченного решения; Вы можете использовать perfy, который использует высокое разрешение в реальном времени ( process.hrtime) для внутреннего использования.

var perfy = require('perfy');

function end(label) {
    return function (err, saved) {
        console.log(err ? 'Error' : 'Saved'); 
        console.log( perfy.end(label).time ); // <——— result: seconds.milliseconds
    };
}

for (var i = 1; i < LIMIT; i++) {
    var label = 'db-save-' + i;
    perfy.start(label); // <——— start and mark time
    db.users.save({ id: i, name: 'MongoUser [' + i + ']' }, end(label));
}

Обратите внимание, что каждый раз, когда perfy.end(label)вызывается, этот экземпляр автоматически уничтожается.

Раскрытие: Написал этот модуль, вдохновленный ответом Д.Дерисо . Документы здесь .


2

Вы можете попробовать Benchmark.js . Он поддерживает множество платформ, в том числе node.js.


11
Было бы хорошо, если бы вы могли добавить пример того, как использовать benchmark.js для этого варианта использования.
Петах

2

Вы также можете попробовать exectimer . Это дает вам обратную связь, как:

var t = require("exectimer");

var myFunction() {
   var tick = new t.tick("myFunction");
   tick.start();
   // do some processing and end this tick
   tick.stop();
}

// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.myFunction.min()); // minimal tick duration
console.log(t.timers.myFunction.max()); // maximal tick duration
console.log(t.timers.myFunction.mean()); // mean tick duration
console.log(t.timers.myFunction.median()); // median tick duration

[править] Теперь есть даже более простой способ использовать exectimer, потому что теперь он может переносить код, который будет измеряться. Ваш код может быть упакован так:

var t = require('exectimer'),
Tick = t.Tick;

for(var i = 1; i < LIMIT; i++){
    Tick.wrap(function saveUsers(done) {
        db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
            if( err || !saved ) console.log("Error");
            else console.log("Saved");
            done();
        });
    });
}

// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.saveUsers.min()); // minimal tick duration
console.log(t.timers.saveUsers.max()); // maximal tick duration
console.log(t.timers.saveUsers.mean()); // mean tick duration
console.log(t.timers.saveUsers.median()); // median tick duration

1

У меня была такая же проблема при переходе с AWS на Azure

Для экспресс & aws вы уже можете использовать существующие time () и timeEnd ()

Для Azure используйте это: https://github.com/manoharreddyporeddy/my-nodejs-notes/blob/master/performance_timers_helper_nodejs_azure_aws.js

Эти time () и timeEnd () используют существующую функцию hrtime (), которая обеспечивает высокое разрешение в реальном времени.

Надеюсь это поможет.


0

И еще один вариант - использовать инструмент экспресс-отладки :

Экспресс-отладка является инструментом разработки для экспресс. Это простое промежуточное программное обеспечение, которое вносит полезный отладочный вывод в ваш html, не мешая.

Удобно предлагает профилирующую панель:

общее время обработки запроса. промежуточное ПО, параметры и временные параметры маршрута.

Также. чтобы добавить к ответам выше, вы можете проверить этот ответ, чтобы включить любой профилирующий код только для среды разработки.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.