Pengaturan waktu mikrodetik di JavaScript

100

Apakah ada fungsi pengaturan waktu dalam JavaScript dengan resolusi mikrodetik?

Saya mengetahui timer.js untuk Chrome, dan berharap akan ada solusi untuk browser ramah lainnya, seperti Firefox, Safari, Opera, Epiphany, Konqueror, dll. Saya tidak tertarik untuk mendukung IE apa pun, tetapi jawaban termasuk IE dipersilakan.

(Mengingat akurasi waktu milidetik yang buruk di JS, saya tidak menahan napas untuk yang satu ini!)

Pembaruan: timer.js mengiklankan resolusi mikrodetik, tetapi itu hanya mengalikan pembacaan milidetik dengan 1.000. Diverifikasi dengan pengujian dan inspeksi kode. Kecewa. : [

mwcz.dll
sumber
2
Apa yang Anda coba lakukan di browser yang membutuhkan akurasi mikrodetik? Secara umum jaminan kinerja dari perilaku browser tidak begitu tepat.
Yuliy
4
Tidak akan terjadi. Anda tidak dapat mempercayai akurasi mikro detik sama sekali meskipun itu ada. Satu-satunya kasus penggunaan solid yang dapat saya bayangkan adalah klien asli di chrome tetapi Anda tidak peduli dengan JS API. Juga suka memperlakukan "Epiphany" sebagai browser kelas satu dan mengabaikan IE.
Raynos
6
'Mendapatkan' waktu dalam javascript membutuhkan waktu, seperti halnya mengembalikannya- dan latensi meningkat jika Anda berada di laman web yang menggambar ulang atau menangani peristiwa. Saya bahkan tidak akan mengandalkan akurasi 10 milidetik terdekat.
kennebec
1
Misalnya, memunculkan popup dengan kecepatan super tinggi? Pada dasarnya, masalahnya adalah memberikan pihak luar terlalu banyak akses ke mesin pengguna hanya karena fakta bahwa seseorang mengunjungi situs web adalah masalah serius.
Pointy
1
Ini tidak lebih "rentan" daripada setInterval (popup, 0), yang cukup cepat sehingga masalahnya pada dasarnya setara. Haruskah akurasi milidetik juga dihapus? kennebec: komentar Anda masuk akal, terima kasih.
mwcz

Jawaban:

134

Seperti yang disinggung dalam jawaban Mark Rejhon, ada API yang tersedia di browser modern yang menampilkan data pengaturan waktu resolusi sub-milidetik ke skrip: W3C High Resolution Timer , alias window.performance.now().

now()lebih baik daripada tradisional Date.getTime()dalam dua hal penting:

  1. now()adalah ganda dengan resolusi submillisecond yang mewakili jumlah milidetik sejak awal navigasi halaman. Ini mengembalikan jumlah mikrodetik dalam pecahan (misalnya nilai 1000,123 adalah 1 detik dan 123 mikrodetik).

  2. now()meningkat secara monoton. Ini penting karena mungkinDate.getTime() dapat melompat maju atau bahkan mundur pada panggilan berikutnya. Khususnya, jika waktu sistem OS diperbarui (misalnya sinkronisasi jam atom), juga diperbarui. dijamin akan selalu meningkat secara monoton, sehingga tidak terpengaruh oleh waktu sistem OS - ini akan selalu menjadi waktu jam dinding (dengan asumsi jam dinding Anda tidak atom ...).Date.getTime()now()

now()dapat digunakan di hampir semua tempat itu new Date.getTime(), + new Datedan Date.now()sedang. Pengecualiannya adalah Datedan now()waktu tidak dapat digabungkan, karena Datedidasarkan pada unix-epoch (jumlah milidetik sejak 1970), sedangkan now()adalah jumlah milidetik sejak navigasi halaman Anda dimulai (jadi akan jauh lebih kecil dari Date).

now()didukung di Chrome stable, Firefox 15+, dan IE10. Ada juga beberapa polyfill yang tersedia.

NicJ
sumber
1
polyfill kemungkinan besar akan menggunakan Date.now (), jadi ini masih merupakan opsi terbaik mengingat IE9 dan jutaan penggunanya, mengapa mencampur pustaka pihak ketiga kemudian
Vitaliy Terziev
4
Jam dinding saya adalah atom.
programmer5000
4
new Date.getTime()bukanlah apa-apa. new Date().getTime()adalah.
The Qodesmith
Saya sangat menyukai tanggapan ini. Saya menjalankan beberapa tes dan mendapatkan contoh yang dapat Anda masukkan ke konsol Anda untuk melihat bahwa ini masih akan memiliki benturan yang dramatis saat menggunakan ini. (perhatikan bahwa saya mendapatkan 10% tabrakan pada mesin yang bagus bahkan dengan melakukan sesuatu yang mahal console.logsetiap kali dijalankan) Sulit dilihat tetapi salin semua kode yang disorot di sini:last=-11; same=0; runs=100; for(let i=0;i<runs;i++) { let now = performance.now(); console.log('.'); if (now === last) { same++; } last = now; } console.log(same, 'were the same');
bladnman
2
Meninjau kembali komentar tahun 2012 saya . performance.now () sekarang agak kabur lagi oleh solusi Meltdown / Spectre. Beberapa browser telah sangat menurunkan kinerja.now () karena alasan keamanan. Saya pikir teknik saya mungkin telah mendapatkan kembali relevansinya lagi untuk banyak kasus penggunaan pembandingan yang sah, tunduk pada batasan waktu-fuzz.
Meskipun demikian
20

Sekarang ada metode baru untuk mengukur mikrodetik di javascript: http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

Namun, di masa lalu, saya menemukan metode kasar untuk mendapatkan presisi 0,1 milidetik dalam JavaScript dari timer milidetik. Mustahil? Nggak. Teruskan membaca:

Saya melakukan beberapa eksperimen presisi tinggi yang memerlukan akurasi pengatur waktu yang diperiksa sendiri, dan menemukan bahwa saya dapat memperoleh presisi 0,1 milidetik secara andal dengan browser tertentu pada sistem tertentu.

Saya telah menemukan bahwa di browser web modern yang dipercepat GPU pada sistem cepat (mis. I7 quad core, di mana beberapa core menganggur, hanya jendela browser) - Saya sekarang dapat mempercayai pengatur waktu agar akurat dalam milidetik. Faktanya, ini menjadi sangat akurat pada sistem i7 yang menganggur, saya bisa dengan andal mendapatkan milidetik yang sama persis, lebih dari 1.000 percobaan. Hanya ketika saya mencoba melakukan hal-hal seperti memuat halaman web tambahan, atau lainnya, akurasi milidetik menurun (Dan saya berhasil menangkap akurasi saya sendiri yang terdegradasi dengan melakukan pemeriksaan waktu sebelum dan sesudah, untuk melihat apakah waktu pemrosesan saya tiba-tiba diperpanjang menjadi 1 milidetik atau lebih - ini membantu saya membatalkan hasil yang mungkin terlalu terpengaruh oleh fluktuasi CPU).

Ini menjadi sangat akurat di beberapa peramban yang dipercepat GPU pada sistem i7 quad-core (ketika jendela peramban adalah satu-satunya jendela), sehingga saya menemukan saya berharap dapat mengakses pengatur waktu presisi 0,1 md dalam JavaScript, karena akurasinya akhirnya sekarang ada pada beberapa sistem penjelajahan kelas atas untuk membuat presisi pengatur waktu tersebut bermanfaat untuk jenis aplikasi khusus tertentu yang membutuhkan presisi tinggi, dan di mana aplikasi tersebut dapat memverifikasi sendiri untuk penyimpangan akurasi.

Tentunya jika Anda melakukan beberapa gerakan printhead, Anda cukup menjalankan beberapa gerakan printhead (misalnya 10 gerakan) kemudian membagi dengan 10 untuk mendapatkan presisi 0,1 milidetik. Itu adalah metode umum untuk mendapatkan presisi yang lebih baik - lakukan beberapa gerakan printhead dan bagi total waktu dengan jumlah gerakan printhead.

NAMUN ... Jika saya hanya dapat melakukan satu lulus benchmark dari tes tertentu karena situasi unik yang tidak biasa, saya menemukan bahwa saya bisa mendapatkan presisi 0,1 (Dan terkadang 0,01ms) dengan melakukan ini:

Inisialisasi / Kalibrasi:

  1. Jalankan loop sibuk untuk menunggu hingga timer bertambah ke milidetik berikutnya (sejajarkan timer ke awal interval milidetik berikutnya) Loop sibuk ini berlangsung kurang dari satu milidetik.
  2. Jalankan loop sibuk lainnya untuk menaikkan penghitung sambil menunggu timer bertambah. Penghitung memberi tahu Anda berapa banyak kenaikan penghitung yang terjadi dalam satu milidetik. Loop sibuk ini berlangsung satu milidetik penuh.
  3. Ulangi langkah di atas, hingga angkanya menjadi sangat stabil (waktu pemuatan, penyusun JIT, dll). 4. CATATAN: Stabilitas nomor memberi Anda ketepatan yang dapat dicapai pada sistem idle. Anda dapat menghitung varians, jika Anda perlu memeriksa sendiri ketepatannya. Variansnya lebih besar di beberapa browser, dan lebih kecil di browser lain. Lebih besar pada sistem yang lebih cepat dan lebih lambat pada sistem yang lebih lambat. Konsistensi juga bervariasi. Anda dapat mengetahui browser mana yang lebih konsisten / akurat daripada yang lain. Sistem yang lebih lambat dan sistem yang sibuk akan menyebabkan perbedaan yang lebih besar antara lintasan inisialisasi. Ini dapat memberi Anda kesempatan untuk menampilkan pesan peringatan jika browser tidak memberi Anda cukup presisi untuk memungkinkan pengukuran 0,1 md atau 0,01 md. Pengatur waktu miring dapat menjadi masalah, tetapi beberapa pengatur waktu milidetik bilangan bulat pada beberapa peningkatan sistem cukup akurat (cukup tepat di titik), yang akan menghasilkan nilai kalibrasi yang sangat konsisten yang dapat Anda percayai.
  4. Simpan nilai penghitung akhir (atau rata-rata dari beberapa kalibrasi terakhir)

Pembandingan satu lintasan ke presisi sub-milidetik:

  1. Jalankan loop sibuk untuk menunggu hingga timer bertambah ke milidetik berikutnya (sejajarkan timer ke awal interval milidetik berikutnya). Loop sibuk ini berlangsung kurang dari satu milidetik.
  2. Jalankan tugas yang ingin Anda tolak ukur dengan tepat waktu.
  3. Periksa pengatur waktu. Ini memberi Anda milidetik integer.
  4. Jalankan loop sibuk terakhir untuk menaikkan penghitung sambil menunggu timer bertambah. Loop sibuk ini berlangsung kurang dari satu milidetik.
  5. Bagilah nilai pencacah ini, dengan nilai pencacah asli dari inisialisasi.
  6. Sekarang Anda mendapatkan bagian desimal dari milidetik !!!!!!!!

PERINGATAN: Loop sibuk TIDAK disarankan di browser web, tetapi untungnya, loop sibuk ini masing-masing berjalan kurang dari 1 milidetik, dan hanya dijalankan beberapa kali.

Variabel seperti kompilasi JIT dan fluktuasi CPU menambah ketidakakuratan yang sangat besar, tetapi jika Anda menjalankan beberapa inisialisasi, Anda akan memiliki kompilasi ulang dinamis penuh, dan akhirnya penghitung mengatur sesuatu yang sangat akurat. Pastikan semua loop sibuk memiliki fungsi yang persis sama untuk semua kasus, sehingga perbedaan loop sibuk tidak menyebabkan perbedaan. Pastikan semua baris kode dijalankan beberapa kali sebelum Anda mulai mempercayai hasilnya, untuk memungkinkan compiler JIT telah distabilkan ke kompilasi ulang dinamis penuh (dynarec).

Nyatanya, saya menyaksikan presisi mendekati mikrodetik pada sistem tertentu , tapi saya belum mempercayainya. Tapi ketepatan 0,1 milidetik tampaknya bekerja cukup andal, pada sistem quad-core yang menganggur di mana saya satu-satunya halaman browser. Saya sampai pada kasus uji ilmiah di mana saya hanya bisa melakukan satu kali operan (karena variabel unik yang terjadi), dan perlu menghitung waktu setiap operan secara tepat, daripada menghitung rata-rata beberapa operan berulang, jadi itulah mengapa saya melakukan ini.

Saya melakukan beberapa pre-pass dan dummy pass (juga untuk menyelesaikan dynarec), untuk memverifikasi keandalan presisi 0,1ms (tetap solid selama beberapa detik), kemudian menjauhkan tangan saya dari keyboard / mouse, sementara benchmark terjadi, kemudian melakukan beberapa pasca-operan untuk memverifikasi keandalan presisi 0,1ms (tetap kokoh lagi). Ini juga memverifikasi bahwa hal-hal seperti perubahan status daya, atau hal-hal lain, tidak terjadi antara sebelum dan sesudah, yang mengganggu hasil. Ulangi pre-test dan post-test antara setiap lulus benchmark. Atas hal ini, saya cukup yakin bahwa hasil di antaranya akurat. Tidak ada jaminan, tentu saja, tetapi ini menunjukkan bahwa presisi <0.1ms yang akurat dimungkinkan dalam beberapa kasus di browser web.

Metode ini hanya berguna dalam kasus yang sangat, sangat khusus . Meski begitu, itu benar-benar tidak akan 100% dijamin tanpa batas, Anda bisa mendapatkan akurasi yang sangat dapat dipercaya, dan bahkan keakuratan ilmiah bila dikombinasikan dengan beberapa lapisan verifikasi internal dan eksternal.

Mark Rejhon
sumber
3
Dulu rumit untuk melakukan pengaturan waktu dengan presisi tinggi karena yang kami miliki hanyalah Date.now()atau +new Date(). Tapi sekarang kita punya performance.now(). Meskipun jelas Anda telah menemukan beberapa cara keren untuk meretas lebih banyak kemampuan, jawaban ini pada dasarnya sudah usang. Selain itu, jangan rekomendasikan apa pun yang terkait dengan loop sibuk. Jangan lakukan itu. Kami tidak membutuhkan lebih dari itu.
Steven Lu
1
Kebanyakan browser mengurangi ketepatan implementasi performance.now () mereka untuk sementara mengurangi serangan waktu cache. Saya bertanya-tanya apakah jawaban ini masih memiliki arti penting dalam penelitian keamanan.
Qi Fan
2
Meninjau kembali komentar saya sendiri. Wow, saya memposting di atas pada tahun 2012 jauh sebelum performance.now (). Tapi sekarang itu agak kabur lagi sedikit oleh solusi Meltdown / Spectre. Beberapa browser telah sangat menurunkan kinerja.now () karena alasan keamanan. Saya pikir teknik di atas mungkin telah mendapatkan kembali beberapa relevansi lagi untuk banyak kasus penggunaan benchmarking yang sah, tunduk pada batasan timer-fuzz.
Mark Rejhon
3

Secara umum jawabannya adalah "tidak". Jika Anda menggunakan JavaScript di beberapa lingkungan sisi server (yaitu, bukan di browser), maka semua taruhan dibatalkan dan Anda dapat mencoba melakukan apa pun yang Anda inginkan.

edit - jawaban ini sudah tua; standar telah berkembang dan fasilitas yang lebih baru tersedia sebagai solusi untuk masalah waktu yang akurat. Meski begitu, harus diingat bahwa di luar domain dari sistem operasi real-time yang sebenarnya, kode non-hak istimewa biasa memiliki kontrol terbatas atas aksesnya ke sumber daya komputasi. Mengukur kinerja tidak sama (harus) dengan memprediksi kinerja.

Runcing
sumber
2

Berikut adalah contoh yang menunjukkan timer resolusi tinggi saya untuk node.js :

 function startTimer() {
   const time = process.hrtime();
   return time;
 }

 function endTimer(time) {
   function roundTo(decimalPlaces, numberToRound) {
     return +(Math.round(numberToRound + `e+${decimalPlaces}`)  + `e-${decimalPlaces}`);
   }
   const diff = process.hrtime(time);
   const NS_PER_SEC = 1e9;
   const result = (diff[0] * NS_PER_SEC + diff[1]); // Result in Nanoseconds
   const elapsed = result * 0.0000010;
   return roundTo(6, elapsed); // Result in milliseconds
 }

Pemakaian:

 const start = startTimer();

 console.log('test');

 console.log(`Time since start: ${endTimer(start)} ms`);

Biasanya, Anda mungkin bisa menggunakan:

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

Jika Anda adalah bagian waktu dari kode yang melibatkan perulangan, Anda tidak dapat memperoleh akses ke nilai console.timeEnd()untuk menambahkan hasil pengatur waktu Anda bersama-sama. Anda bisa, tetapi itu menjadi buruk karena Anda harus memasukkan nilai variabel iterasi Anda, seperti i, dan menetapkan kondisi untuk mendeteksi jika pengulangan selesai.

Berikut ini contohnya karena bisa bermanfaat:

 const num = 10;

 console.time(`Time til ${num}`);

 for (let i = 0; i < num; i++) {
   console.log('test');
   if ((i+1) === num) { console.timeEnd(`Time til ${num}`); }
   console.log('...additional steps');
 }

Kutip: https://nodejs.org/api/process.html#process_process_hrtime_time

agm1984
sumber