Bagaimana cara mengukur waktu eksekusi kode JavaScript dengan panggilan balik?


319

Saya memiliki kode JavaScript yang saya jalankan menggunakan node.jsinterpreter.

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

Bagaimana saya bisa mengukur waktu yang dibutuhkan oleh operasi penyisipan basis data ini? Saya dapat menghitung perbedaan nilai tanggal setelah dan sebelum potongan kode ini tetapi itu tidak benar karena sifat asinkron dari kode.


8
Baca saja waktu mulai sebelum panggilan db, dan waktu akhir DI DALAM panggilan balik ..
BFil

Ada kemungkinan bahwa waktu DB selesai memasukkan dan waktu panggil balik dijalankan tidak sama dan ini akan menimbulkan kesalahan dalam pengukuran?
Stormshadow

1
Tidak, Anda tidak perlu khawatir tentang hal itu, jika kode perpustakaan db dirancang dengan baik dan tidak menangani operasi lain sebelum mengaktifkan callback, Anda harus mendapatkan ukuran yang baik. Anda juga dapat membuat profil penyisipan dengan meletakkan stempel waktu di dalam kode perpustakaan tempat penyisipan benar-benar dilakukan, bukan milik Anda sendiri, tetapi, sekali lagi, saya tidak akan khawatir tentang hal itu
BFil

Saya akan merekomendasikan mencoba NodeTime yang tampaknya cocok untuk apa yang Anda coba lakukan.
Julian Knight

Saya menulis timerlogyang mirip console.time()tetapi dengan fitur tambahan; github.com/brillout/timerlog
brillout

Jawaban:


719

Gunakan Node.js console.time()dan 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
Solusi bersih dan bawaan untuk simpul.
Behlül Uçar

45
> Saya ingin tahu bagaimana mengukur waktu yang dibutuhkan oleh operasi penyisipan db ini. --- console.timeEnd ("dbsave") hanya menampilkan untuk menghibur waktu. Anda tidak dapat menggunakannya lebih lanjut dan kurang fleksibel. Jika Anda membutuhkan nilai waktu aktual, seperti dalam pertanyaan asli, Anda tidak dapat menggunakan console.timeEnd ("dbsave")
gogaman

@ gogaman ini adalah poin yang bagus, karena Anda tidak dapat menangkap output dari console.timeEnd (). Mungkin mungkin berguna untuk mem-pipe output ke file dan memanfaatkannya dari sana?
Doug Molineux

5
Jadi apa perbedaan antara console.time () dan process.hrtime () dalam jawaban di bawah ini?
santo kuning

3
Layak menambahkan catatan bahwa waktu pelaksanaan kemudian dicetak, hanya agar pengguna baru sekarang.
janko-m

208

Ada metode yang dirancang untuk ini. Periksa process.hrtime (); .

Jadi, pada dasarnya saya menempatkan ini di bagian atas aplikasi saya.

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
}

Lalu saya menggunakannya untuk melihat berapa lama waktu yang dibutuhkan. Berikut adalah contoh dasar yang mencetak konten file teks yang disebut "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);

Inilah tes cepat yang dapat Anda jalankan di terminal (BASH shell):

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

3
apakah itu lebih unggul daripada solusi console.time dengan cara apa pun?
scravy

31
Ya, ini jauh lebih tepat dan Anda dapat menyimpan hasilnya dalam variabel
Dallas Clark

Yang ini berfungsi untuk saya, karena saya ingin memanggil timer beberapa kali
tbh__

2
Mengapa Anda menelepon process.hrtime(start)dua kali? Apakah ada alasan khusus untuk itu?
Sohail Si

1
process.hrtime ([time]), di mana waktu adalah parameter opsional yang harus merupakan hasil dari process.hrtime () panggilan sebelumnya untuk berbeda dengan waktu saat ini. Ini memberikan perbedaan antara panggilan saat ini dan panggilan hrtime sebelumnya.
Nilesh Jain

72

Memanggil console.time('label')akan merekam waktu saat ini dalam milidetik, kemudian panggilan console.timeEnd('label')akan menampilkan durasi dari titik itu.

Waktu dalam milidetik akan dicetak secara otomatis di samping label, jadi Anda tidak perlu membuat panggilan terpisah ke console.log untuk mencetak label:

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

Untuk informasi lebih lanjut, lihat dokumen pengembang Mozilla diconsole.time .


Apa yang ini tambahkan ke jawaban yang diterima ?
Dan Dascalescu

1
Jawaban yang diterima diubah setelah jawaban saya untuk menggunakan kode saya
jfcorugedo

24

Terkejut belum ada yang menyebutkan perpustakaan yang baru dibangun:

Tersedia di Node> = 8.5, dan harus di Browser Modern

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

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

Node 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

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

1
Memberi saya TypeError: performance.getEntriesByName is not a functiondi Node v10.4.1
Jeremy Thille

Saya membuat contoh sehingga Anda dapat menjalankannya secara online. Ini adalah Node 9.7.1. Jika tidak bekerja di v10.4.1 maka saya ingin tahu apa yang mungkin berubah!
Cody G

1
Stability: 1 - Experimentalmungkin? :) nodejs.org/docs/latest-v8.x/api/…
Jeremy Thille

Ya pasti sudah berubah. Ada pengamat baru di v10, Anda dapat melihat dokumen di nodejs.org/docs/latest-v10.x/api/documentation.html . Saya akan memperbarui ketika saya mendapat kesempatan!
Cody G

19

Bagi siapa pun yang ingin mendapatkan nilai waktu yang berlalu alih-alih output konsol:

gunakan process.hrtime () sebagai saran @ D.Deriso, di bawah ini adalah pendekatan saya yang lebih sederhana:

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
Saya harus mencari sintaks '+ Tanggal baru ()' untuk mencari tahu apa artinya itu. Menurut komentar pada jawaban ini ( stackoverflow.com/a/221565/5114 ), itu bukan ide yang baik untuk menggunakan formulir itu untuk alasan kinerja serta keterbacaan. Saya lebih suka sesuatu yang sedikit lebih verbose sehingga lebih jelas bagi pembaca. Lihat juga jawaban ini: stackoverflow.com/a/5036460/5114
Mnebuerquo

3
Saya sering menggunakan var start = process.hrtime(); ... var end = process.hrtime(start);untuk mendapatkan waktu resolusi tinggi (jika saya perlu mengharapkan akurasi sub milidetik)
Andrey Sidorov

9

Pertanyaan lama tetapi untuk API sederhana dan solusi ringan; Anda dapat menggunakan perfy yang menggunakan real time resolusi tinggi ( process.hrtime) secara internal.

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

Perhatikan bahwa setiap kali perfy.end(label)dipanggil, instance itu dihancurkan secara otomatis.

Pengungkapan: Menulis modul ini, terinspirasi oleh jawaban D.Deriso . Dokumen di sini .


2

Anda bisa mencoba Benchmark.js . Ini mendukung banyak platform di antara mereka juga node.js.


11
Akan lebih baik jika Anda bisa menambahkan contoh cara menggunakan benchmark.js untuk kasus penggunaan ini.
Petah

2

Anda juga dapat mencoba exectimer . Ini memberi Anda umpan balik seperti:

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

[Sunting] Bahkan ada cara yang lebih sederhana sekarang untuk menggunakan exectimer karena sekarang dapat membungkus kode yang akan diukur. Kode Anda dapat dibungkus seperti ini:

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


0

Dan opsi lain adalah menggunakan alat debug-cepat :

express-debug adalah alat pengembangan untuk express. Ini middleware sederhana yang menyuntikkan output debug yang berguna ke html Anda, dengan cara yang tidak menghalangi.

Dengan mudah menawarkan panel profil:

total waktu pemrosesan req. middleware, param, dan timing rute.

Juga. untuk menambahkan jawaban di atas, Anda dapat memeriksa jawaban ini untuk mengaktifkan kode profil apa pun hanya untuk lingkungan pengembangan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.