JavaScript'te mikrosaniye zamanlaması


101

JavaScript'te mikrosaniye çözünürlükte herhangi bir zamanlama işlevi var mı?

Chrome için timer.js'nin farkındayım ve Firefox, Safari, Opera, Epiphany, Konqueror gibi diğer kullanıcı dostu tarayıcılar için bir çözüm olacağını umuyorum. Herhangi bir IE'yi desteklemekle ilgilenmiyorum, ancak IE dahil yanıtlar hoş geldiniz.

(JS'deki milisaniye zamanlamanın zayıf doğruluğu göz önüne alındığında, nefesimi bu sefer tutmuyorum!)

Güncelleme: timer.js, mikrosaniye çözünürlüğü bildirir, ancak milisaniye okumasını 1.000 ile çarpar. Test ve kod incelemesi ile doğrulanmıştır. Hayal kırıklığına uğramış. : [


2
Mikrosaniye doğruluğu gerektiren bir tarayıcıda ne yapmaya çalışıyorsunuz? Genel olarak, tarayıcıların davranışlarının performans garantileri o kadar kesin değildir.
Yuliy

4
Olmayacak. Mevcut olsa bile mikro saniye doğruluğuna hiç güvenemezsiniz. Hayal edebileceğim tek sağlam kullanım durumu, Chrome'daki yerel istemcilerdir, ancak o zaman JS API'yi umursamıyorsunuz. Ayrıca "Epiphany" ye birinci sınıf bir tarayıcı gibi davranmayı ve IE'yi görmezden gelmeyi seviyorum.
Raynos

6
Javascript'te zamanı 'almak', onu döndürmek gibi biraz zaman alır - ve olayları yeniden çizen veya işleyen bir web sayfasındaysanız gecikme artar. En yakın 10 milisaniyelik doğruluğa bile güvenmem.
kennebec

1
Mesela, pop-up'ları süper yüksek hızda kusmak gibi mi? Temelde sorun, yalnızca bir kişinin bir web sitesini ziyaret etmesi nedeniyle harici tarafların kullanıcı makinelerine çok fazla erişim sağlamasının ciddi bir sorun olmasıdır.
Sivri

1
SetInterval'den (popup, 0) daha "savunmasız" değildir, bu da sorunun temelde eşdeğer olduğu kadar hızlıdır. Milisaniye doğruluğu da kaldırılmalı mı? kennebec: yorumunuz mantıklı, teşekkürler.
mwcz

Yanıtlar:


135

Mark Rejhon'un cevabında belirtildiği gibi, modern tarayıcılarda bir milisaniyenin altındaki çözünürlük zamanlama verilerini komut dosyasına gösteren bir API var: W3C Yüksek Çözünürlüklü Zamanlayıcı , aka window.performance.now().

now()Date.getTime()iki önemli yönden gelenekselden daha iyidir :

  1. now()sayfada gezinmenin başlangıcından bu yana geçen milisaniye sayısını temsil eden, milisaniye altı çözünürlüğe sahip bir çifttir. Kesirli olarak mikrosaniye sayısını döndürür (ör. 1000.123 değeri 1 saniye ve 123 mikrosaniyedir).

  2. now()monoton bir şekilde artıyor. Olarak, bu önemlidir Date.getTime()olabilir muhtemelen izleyen çağrılarda ileri geri ya da atlamak. Özellikle, işletim sisteminin sistem saati güncellenirse (örneğin atomik saat senkronizasyonu) Date.getTime()da güncellenir. now()her zaman monoton bir şekilde artması garanti edilir, bu nedenle işletim sisteminin sistem saatinden etkilenmez - her zaman duvar saati zamanı olacaktır (duvar saatinizin atomik olmadığı varsayılarak ...).

now()kullanılan hemen hemen her yerde olduğunu edilebilir new Date.getTime(), + new Dateve Date.now()vardır. İstisna olduğunu Dateve now()yanı kez karışmaz Datedayanmaktadır Unix dönemin (1970 yılından beri milisaniye sayısı) ise, now()(o çok daha küçük olacak böylece sayfa gezinti başladığından beri milisaniye sayısıdır Date).

now()Chrome kararlı, Firefox 15+ ve IE10'da desteklenmektedir. Ayrıca birkaç çoklu dolgu da mevcuttur.


1
polyfill'ler büyük olasılıkla Date.now () kullanacaktır, bu nedenle IE9 ve milyonlarca kullanıcısı düşünüldüğünde hala en iyi seçenek budur, o halde neden üçüncü taraf kitaplığı karıştırılıyor
Vitaliy Terziev

4
Benim duvar saati olan atom.
programmer5000

4
new Date.getTime()bir şey değil. new Date().getTime()dır-dir.
The Qodesmith

Bu yanıtı gerçekten beğendim. Birkaç test yaptım ve bunu kullanırken hala dramatik çarpışmalara neden olacağını görmek için konsolunuza bırakabileceğiniz bir örnek buldum. ( console.logHer seferindeki kadar pahalı bir şey yapsam bile iyi bir makinede% 10 çarpışma elde ettiğimi not edin ) 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');
Anlamak

2
2012 yılı yorumumu tekrar gözden geçiriyorum . performance.now () artık Meltdown / Spectre geçici çözümleriyle biraz daha bulanık hale geldi. Bazı tarayıcılar, güvenlik nedenleriyle performance.now () 'u ciddi şekilde düşürmüştür. Sanırım benim tekniğim, zamanlayıcı-bulanıklık sınırlamalarına tabi olan birçok meşru kıyaslama kullanım durumunun büyük bir kısmı için muhtemelen yeniden bir miktar alaka kazandı. Bununla birlikte, bazı tarayıcıların artık 2012'de mevcut olmayan bazı geliştirici performans profili oluşturma özellikleri / uzantıları var.
Mark Rejhon

20

Artık javascript'te mikrosaniyeleri ölçmenin yeni bir yöntemi var: http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

Ancak geçmişte, JavaScript'te bir milisaniye zamanlayıcıdan 0.1 milisaniye hassasiyet elde etmenin kaba bir yöntemini buldum. İmkansız mı? Hayır! Okumaya devam et:

Kendinden kontrol edilen zamanlayıcı doğruluklarını gerektiren bazı yüksek hassasiyetli deneyler yapıyorum ve belirli sistemlerde belirli tarayıcılarda 0,1 milisaniye hassasiyetini güvenilir bir şekilde elde edebildiğimi keşfettim.

Hızlı sistemlerdeki modern GPU hızlandırmalı web tarayıcılarında (örneğin, birkaç çekirdeğin boşta olduğu i7 dört çekirdekli, yalnızca tarayıcı penceresi) - artık zamanlayıcıların milisaniye hassasiyetinde olduğuna güvenebilirim. Aslında, boşta olan bir i7 sisteminde o kadar doğru hale geldi ki, aynı milisaniyeyi 1000'den fazla denemede güvenilir bir şekilde elde edebildim. Yalnızca fazladan bir web sayfası veya başka bir şey yüklemek gibi şeyler yapmaya çalıştığımda, milisaniye doğruluğu azalır (Ve önce-sonra zaman kontrolü yaparak kendi düşük doğruluğumu başarılı bir şekilde yakalayabilirim. işlem sürem aniden 1 veya daha fazla milisaniyeye uzadı - bu, muhtemelen CPU dalgalanmalarından çok olumsuz etkilenen sonuçları geçersiz kılmama yardımcı oluyor).

İ7 dört çekirdekli sistemlerde (tarayıcı penceresi tek pencere olduğunda) bazı GPU hızlandırmalı tarayıcılarda o kadar doğru hale geldi ki, JavaScript'te 0.1 ms hassas zamanlayıcıya erişebilmeyi dilediğimi fark ettim, çünkü doğruluk nihayet şimdi geldi yüksek hassasiyet gerektiren ve uygulamaların doğruluk sapmaları için kendi kendini doğrulayabildiği belirli türdeki niş uygulamalar için bu tür zamanlayıcı hassasiyetini değerli kılmak için bazı ileri teknoloji tarama sistemlerinde var.

Açıktır ki, birkaç geçiş yapıyorsanız, birden çok geçiş (örneğin 10 geçiş) çalıştırabilir ve ardından 0,1 milisaniye hassasiyet elde etmek için 10'a bölebilirsiniz. Bu, daha iyi hassasiyet elde etmenin yaygın bir yöntemidir - birden çok geçiş yapın ve toplam süreyi geçiş sayısına bölün.

ANCAK ... Alışılmadık derecede benzersiz bir durum nedeniyle belirli bir testin yalnızca tek bir kıyaslama geçişini yapabilirsem, bunu yaparak 0,1 (ve bazen 0,01 ms) hassasiyet elde edebileceğimi anladım:

Başlatma / Kalibrasyon:

  1. Zamanlayıcı bir sonraki milisaniye artana kadar beklemek için meşgul bir döngü çalıştırın (zamanlayıcıyı bir sonraki milisaniye aralığının başlangıcına hizalayın) Bu meşgul döngü bir milisaniyeden az sürer.
  2. Zamanlayıcının artmasını beklerken bir sayacı artırmak için başka bir meşgul döngü çalıştırın. Sayaç, bir milisaniyede kaç tane sayaç artışı olduğunu söyler. Bu yoğun döngü bir tam milisaniye sürer.
  3. Sayılar son derece kararlı hale gelene kadar (yükleme süresi, JIT derleyicisi, vb.) Yukarıdakileri tekrarlayın. 4. NOT: Sayının kararlılığı, boşta çalışan bir sistemde elde edilebilir hassasiyetinizi verir. Hassasiyeti kendi kendinize kontrol etmeniz gerekirse, varyansı hesaplayabilirsiniz. Farklar bazı tarayıcılarda daha büyük, diğer tarayıcılarda daha küçüktür. Daha hızlı sistemlerde daha büyük ve daha yavaş sistemlerde daha yavaş. Tutarlılık da değişir. Hangi tarayıcıların diğerlerinden daha tutarlı / doğru olduğunu anlayabilirsiniz. Daha yavaş sistemler ve meşgul sistemler, başlatma geçişleri arasında daha büyük farklılıklara yol açacaktır. Tarayıcı size 0,1 ms veya 0,01 ms ölçümlere izin verecek kadar hassaslık sağlamıyorsa, bu size bir uyarı mesajı görüntüleme fırsatı verebilir. Zamanlayıcı çarpıklığı bir sorun olabilir, ancak bazı sistemlerdeki bazı tamsayı milisaniye zamanlayıcıları oldukça doğru bir şekilde artar (tam nokta üzerinde), bu da güvenebileceğiniz çok tutarlı kalibrasyon değerleriyle sonuçlanır.
  4. Son sayaç değerini (veya son birkaç kalibrasyon geçişinin ortalamasını) kaydedin

Bir geçişten milisaniyenin altındaki hassasiyete kıyaslama:

  1. Zamanlayıcı bir sonraki milisaniye artana kadar beklemek için meşgul bir döngü çalıştırın (zamanlayıcıyı bir sonraki milisaniye aralığının başlangıcına hizalayın). Bu yoğun döngü bir milisaniyeden az sürer.
  2. Zamanı tam olarak karşılaştırmak istediğiniz görevi gerçekleştirin.
  3. Zamanlayıcıyı kontrol edin. Bu size tamsayı milisaniye verir.
  4. Zamanlayıcının artmasını beklerken bir sayacı artırmak için son bir meşgul döngüsü çalıştırın. Bu yoğun döngü bir milisaniyeden az sürer.
  5. Bu sayaç değerini, başlatmadan itibaren orijinal sayaç değerine bölün.
  6. Şimdi milisaniyenin ondalık kısmını aldınız !!!!!!!!

UYARI: Meşgul döngüler web tarayıcılarında ÖNERİLMEZ, ancak neyse ki, bu yoğun döngüler her biri 1 milisaniyeden daha az çalışır ve yalnızca birkaç kez çalıştırılır.

JIT derlemesi ve CPU dalgalanmaları gibi değişkenler büyük yanlışlıklar ekler, ancak birkaç başlatma geçişi çalıştırırsanız, tam dinamik yeniden derlemeye sahip olursunuz ve sonunda sayaç çok doğru bir şeye yerleşir. Meşgul döngülerdeki farklılıkların farklılıklara yol açmaması için tüm meşgul döngülerin tüm durumlar için tam olarak aynı işlev olduğundan emin olun. Sonuçlara güvenmeye başlamadan önce, JIT derleyicilerinin tam dinamik yeniden derlemeye (dynarec) stabilize olmasını sağlamak için tüm kod satırlarının birkaç kez çalıştırıldığından emin olun.

Aslında, bazı sistemlerde hassasiyetin mikrosaniyelere yaklaştığına şahit oldum , ancak buna henüz güvenmezdim. Ancak 0.1 milisaniye hassasiyetinin, benim tek tarayıcı sayfası olduğum boşta duran dört çekirdekli bir sistemde oldukça güvenilir bir şekilde çalıştığı görülüyor. Yalnızca tek seferlik geçişler yapabildiğim (ortaya çıkan benzersiz değişkenler nedeniyle) ve birden fazla tekrar geçişinin ortalamasını almak yerine her geçişi tam olarak zamanlamam gerektiği bilimsel bir test vakasına geldim, bu yüzden bunu yaptım.

0,1 ms hassasiyetin güvenilirliğini doğrulamak için (birkaç saniye sağlam kaldım) birkaç ön geçiş ve sahte geçiş yaptım (ayrıca dynarec'i çözmek için), ardından kıyaslama yapılırken ellerimi klavyeden / fareden uzak tuttum, sonra birkaç tane yaptım 0,1 ms hassasiyetin güvenilirliğini doğrulamak için son geçişler (tekrar sabit kaldı). Bu aynı zamanda güç durumu değişiklikleri veya diğer şeyler gibi şeylerin öncesi ve sonrası arasında meydana gelmediğini ve sonuçlara müdahale ettiğini doğrular. Her bir karşılaştırma testi geçişi arasında ön testi ve son testi tekrarlayın. Bunun üzerine aradaki sonuçların doğru olduğundan neredeyse emindim. Elbette bir garanti yoktur, ancak bir web tarayıcısında bazı durumlarda hassas <0,1 ms hassasiyetin mümkün olduğunu gösterir .

Bu yöntem yalnızca çok, çok niş durumlarda kullanışlıdır . Öyle olsa bile, kelimenin tam anlamıyla% 100 sonsuz garanti edilemez, oldukça güvenilir bir doğruluk ve hatta birkaç kat dahili ve harici doğrulama ile birleştirildiğinde bilimsel doğruluk elde edebilirsiniz.


3
Eskiden üstün hassasiyetle zamanlama yapmak karmaşıktı çünkü sahip olduğumuz tek şey Date.now()ya da idi +new Date(). Ama şimdi sahibiz performance.now(). Daha fazla yeteneği kesmek için bazı harika yollar bulduğunuz açık olsa da, bu cevap aslında eskimiştir. Ayrıca, meşgul döngülerle ilgili hiçbir şey önermeyin. Sadece bunu yapma. Daha fazlasına ihtiyacımız yok.
Steven Lu

1
Çoğu tarayıcı, önbellek zamanlama saldırısını geçici olarak azaltmak için performance.now () uygulamalarının hassasiyetini düşürmüştür. Bu cevabın güvenlik araştırmasında hala önemi olup olmadığını merak ediyorum.
Qi Fan

2
Kendi yorumumu tekrar gözden geçiriyorum. Vay canına, 2012 yılında performanstan çok önce yukarıdakileri yayınladım . Şimdi (). Ama şimdi bu, Meltdown / Spectre geçici çözümleriyle biraz daha bulanıklaştı. Bazı tarayıcılar, güvenlik nedenlerinden dolayı performance.now () 'ı ciddi şekilde düşürmüştür. Yukarıdaki tekniğin, zamanlayıcı-bulanıklık sınırlamalarına tabi, birçok meşru kıyaslama kullanım durumunun büyük bir kısmı için muhtemelen yeniden bir miktar alaka kazandığını düşünüyorum.
Mark Rejhon

3

Cevap genel olarak "hayır" dır. JavaScript'i bazı sunucu tarafı ortamında (yani tarayıcıda değil) kullanıyorsanız, tüm bahisler kapalıdır ve istediğiniz her şeyi yapmayı deneyebilirsiniz.

düzenle - bu cevap eski; standartlar ilerledi ve doğru zaman sorununa çözüm olarak daha yeni tesisler mevcut. Öyle bile olsa, gerçek bir gerçek zamanlı işletim sisteminin etki alanı dışında, sıradan, ayrıcalıklı olmayan kodun, hesaplama kaynaklarına erişimi üzerinde sınırlı denetime sahip olduğu unutulmamalıdır. Performansı ölçmek, performansı tahmin etmekle aynı (zorunlu olarak) değildir .


2

İşte node.js için yüksek çözünürlüklü zamanlayıcımı gösteren bir örnek :

 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
 }

Kullanım:

 const start = startTimer();

 console.log('test');

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

Normalde şunları kullanabilirsiniz:

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

Döngü içeren kod bölümlerini zamanlıyorsanız, console.timeEnd()zamanlayıcı sonuçlarınızı birbirine eklemek için değerine erişemezsiniz . Yapabilirsiniz, ancak tiksindirici hale gelir, çünkü gibi yinelenen değişkeninizin değerini enjekte etmeniz ive döngünün bitip bitmediğini algılamak için bir koşul ayarlamanız gerekir.

İşte bir örnek, çünkü faydalı olabilir:

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

Alıntı: https://nodejs.org/api/process.html#process_process_hrtime_time

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.