Як я міряю час виконання коду JavaScript за допомогою зворотних дзвінків?


319

У мене є фрагмент коду 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
Просто прочитайте час початку перед викликом db, а час закінчення
ВНУТРИ

Можливо, час, коли БД закінчує вставку і час виконання зворотного виклику, не є однаковим, і це введе помилку в вимірюванні?
Буря тіні

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

Я рекомендую спробувати NodeTime, який, здається, добре підходить для того, що ви намагаєтеся зробити.
лицар Джуліан

Я написав, timerlogщо схоже, console.time()але з додатковими функціями; github.com/brillout/timerlog
brillout

Відповіді:


718

Використовуйте 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
Чисте і вбудоване рішення для вузла.
Behlül Uçar

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

@gogaman, це хороший момент, оскільки ви не можете зафіксувати вихід з console.timeEnd (). Можливо, це може бути корисно передавати вихід у файл і використовувати звідти?
Doug Molineux

5
Отже, яка різниця між console.time () та process.hrtime () у відповіді нижче?
жовто-святий

3
Варто додати зауваження, що потім час виконання буде надруковано, просто так, щоб тепер з’явилися нові користувачі.
janko-m

208

Існує метод, який призначений для цього. Перевір процес.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)двічі? Чи є певна причина для цього?
Sohail Si

1
process.hrtime ([time]), де час - необов'язковий параметр, який повинен бути результатом попереднього виклику process.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

Для отримання додаткової інформації дивіться документи розробника Mozilla наconsole.time .




24

Здивований, поки ніхто не згадав про нове вбудоване в бібліотеках:

Доступний у вузлі> = 8,5 і має бути в сучасних браузерах

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

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

Вузол 8,5 ~ 9x (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.x

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

Дає мені TypeError: performance.getEntriesByName is not a functionв Node v10.4.1
Jeremy Thille

Я зробив приклад, щоб ви могли запустити його в Інтернеті. Це Вузол 9.7.1. Якщо він не працює в v10.4.1, то мені цікаво, що може змінитися!
Коді G

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

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

19

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

використовуйте process.hrtime () як пропозицію @ D.Deriso, нижче - мій простіший підхід:

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 для цього випадку використання.
Petah

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

Для експрес-посилань ви вже можете використовувати існуючий time () та timeEnd ()

Для Azure використовуйте це: https://github.com/manoharreddyporeddy/my-nodejs-notes/blob/master/performance_timers_helper_nodejs_azure_aws.js

Ці час () і timeEnd () використовують існуючу функцію hrtime (), яка дає реальний час з високою роздільною здатністю.

Сподіваюсь, це допомагає.


0

І ще один варіант - використовувати інструмент експрес-налагодження :

express-debug - це інструмент розвитку експресу. Це просте середнє програмне забезпечення, яке вводить корисний вихід налагодження у ваш html, не обструктивно.

Він зручно пропонує панель для профілювання:

загальний час обробки запиту. проміжне програмне забезпечення, парам і маршрути.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.