Định thời micro giây trong JavaScript


99

Có bất kỳ chức năng định thời nào trong JavaScript với độ phân giải micro giây không?

Tôi biết timer.js dành cho Chrome và tôi hy vọng sẽ có giải pháp cho các trình duyệt thân thiện khác, như Firefox, Safari, Opera, Epiphany, Konqueror, v.v. Tôi không quan tâm đến việc hỗ trợ bất kỳ trình duyệt IE nào, nhưng câu trả lời bao gồm cả IE được hoan nghênh.

(Với độ chính xác kém của thời gian mili giây trong JS, tôi sẽ không nín thở với điều này!)

Cập nhật: timer.js quảng cáo độ phân giải micro giây, nhưng nó chỉ đơn giản là nhân số đọc mili giây với 1.000. Được xác minh bằng thử nghiệm và kiểm tra mã. Thất vọng. : [


2
Bạn đang cố gắng làm gì trong một trình duyệt yêu cầu độ chính xác đến từng micro giây? Nói chung, các đảm bảo hiệu suất hoạt động của các trình duyệt không chính xác như vậy.
Yuliy

4
Sẽ không xảy ra. Bạn không thể tin tưởng độ chính xác từng giây ngay cả khi nó tồn tại. Trường hợp sử dụng vững chắc duy nhất mà tôi có thể tưởng tượng là các ứng dụng khách gốc trong chrome nhưng sau đó bạn không quan tâm đến API JS. Cũng yêu thích việc coi "Epiphany" như một trình duyệt hạng nhất và bỏ qua IE.
Raynos

6
Việc 'lấy' thời gian trong javascript cũng cần một thời gian, cũng như việc trả lại nó- và độ trễ sẽ tăng lên nếu bạn đang ở trên một trang web đang vẽ lại hoặc xử lý các sự kiện. Tôi thậm chí không tin vào độ chính xác 10 mili giây gần nhất.
kennebec

1
Giống như, giả sử, bật các cửa sổ bật lên với tốc độ siêu cao? Về cơ bản, vấn đề là việc cho các bên bên ngoài truy cập quá nhiều vào máy người dùng chỉ đơn thuần là do một người truy cập trang web là một vấn đề nghiêm trọng.
Pointy

1
Nó không "dễ bị tổn thương" hơn setInterval (popup, 0), đủ nhanh để vấn đề về cơ bản là tương đương. Độ chính xác mili giây cũng nên bị loại bỏ? kennebec: nhận xét của bạn có ý nghĩa, cảm ơn bạn.
mwcz

Câu trả lời:


134

Như được ám chỉ trong câu trả lời của Mark Rejhon, có một API có sẵn trong các trình duyệt hiện đại để hiển thị dữ liệu thời gian có độ phân giải dưới mili giây cho tập lệnh: Bộ hẹn giờ độ phân giải cao W3C , hay còn gọi là window.performance.now().

now()tốt hơn so với truyền thống Date.getTime()theo hai cách quan trọng:

  1. now()là một nhân đôi với độ phân giải dưới phần nghìn giây đại diện cho số phần nghìn giây kể từ khi bắt đầu điều hướng trang. Nó trả về số micro giây trong phân số (ví dụ: giá trị 1000.123 là 1 giây và 123 micro giây).

  2. now()đang tăng một cách đơn điệu. Điều này rất quan trọng vì Date.getTime()thể nhảy về phía trước hoặc thậm chí lùi lại trong các cuộc gọi tiếp theo. Đáng chú ý, nếu thời gian hệ thống của HĐH được cập nhật (ví dụ: đồng bộ hóa đồng hồ nguyên tử), Date.getTime()cũng được cập nhật. now()được đảm bảo luôn tăng đơn điệu, vì vậy nó không bị ảnh hưởng bởi thời gian hệ thống của OS - nó sẽ luôn là giờ đồng hồ treo tường (giả sử đồng hồ treo tường của bạn không phải là nguyên tử ...).

now()có thể được sử dụng trong hầu hết mọi nơi new Date.getTime(), + new DateDate.now()đang có. Ngoại lệ là thời gian Datenow()thời gian không kết hợp, vì Datedựa trên unix-epoch (số mili giây kể từ năm 1970), trong khi đó now()là số mili giây kể từ khi điều hướng trang của bạn bắt đầu (vì vậy nó sẽ nhỏ hơn nhiều so vớiDate ).

now()được hỗ trợ trong Chrome ổn định, Firefox 15+ và IE10. Ngoài ra còn có một số polyfills có sẵn.


1
các polyfills sẽ có lẽ hầu hết sử dụng Date.now (), vì vậy đây vẫn là lựa chọn tốt nhất xem xét IE9 và đó là hàng triệu người dùng, tại sao trộn thư viện của bên thứ ba sau đó
Vitaliy Terziev

4
Đồng hồ treo tường của tôi nguyên tử.
lập trình viên

4
new Date.getTime()không phải là một điều. new Date().getTime()Là.
The Qodesmith

Tôi thực sự thích phản hồi này. Tôi đã chạy một vài thử nghiệm và đưa ra một ví dụ mà bạn có thể thả vào bảng điều khiển của mình để thấy rằng điều này vẫn sẽ có những va chạm mạnh khi sử dụng điều này. (lưu ý tôi đã nhận được 10% va chạm trên một máy tốt ngay cả với làm điều gì đó đắt tiền như một console.logtrên mỗi lần chạy) Khó làm ra nhưng sao chép tất cả các mã nêu bật tại đây: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
Xem lại bình luận năm 2012 của tôi . performance.now () bây giờ hơi mờ đi một chút bởi các giải pháp thay thế Meltdown / Spectre. Một số trình duyệt đã xuống cấp nghiêm trọng performance.now () vì lý do bảo mật. Tôi nghĩ rằng kỹ thuật của tôi có lẽ đã lấy lại được một số liên quan một lần nữa đối với rất nhiều trường hợp sử dụng điểm chuẩn hợp pháp, tùy thuộc vào các giới hạn của bộ đếm thời gian. Điều đó nói rằng, một số trình duyệt hiện nay có một số nhà phát triển hiệu suất-profiling tính năng / extensions chưa từng tồn tại vào năm 2012.
Đánh dấu Rejhon

20

Hiện đã có một phương pháp mới để đo micro giây trong javascript: http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

Tuy nhiên, trước đây, tôi đã tìm thấy một phương pháp thô sơ để có được độ chính xác 0,1 mili giây trong JavaScript từ bộ đếm thời gian mili giây. Không thể nào? Không. Hãy đọc tiếp:

Tôi đang thực hiện một số thử nghiệm có độ chính xác cao yêu cầu độ chính xác của bộ hẹn giờ tự kiểm tra và nhận thấy rằng tôi có thể đạt được độ chính xác 0,1 mili giây một cách đáng tin cậy với một số trình duyệt trên một số hệ thống nhất định.

Tôi nhận thấy rằng trong các trình duyệt web hiện đại được tăng tốc bằng GPU trên các hệ thống nhanh (ví dụ như lõi tứ i7, trong đó một số lõi không hoạt động, chỉ có cửa sổ trình duyệt) - giờ tôi có thể tin tưởng bộ đếm thời gian chính xác đến từng mili giây. Trên thực tế, nó trở nên rất chính xác trên một hệ thống i7 nhàn rỗi, tôi đã có thể tin cậy nhận được cùng một phần nghìn giây, qua hơn 1.000 lần thử. Chỉ khi tôi đang cố gắng thực hiện những việc như tải thêm một trang web hoặc cách khác, độ chính xác mili giây mới giảm xuống (Và tôi có thể bắt thành công độ chính xác đã giảm của mình bằng cách kiểm tra trước và sau, để xem liệu thời gian xử lý của tôi đột nhiên bị kéo dài thành 1 hoặc nhiều mili giây - điều này giúp tôi làm mất hiệu lực của các kết quả có thể bị ảnh hưởng quá xấu bởi sự biến động của CPU).

Nó trở nên chính xác đến mức trong một số trình duyệt tăng tốc GPU trên hệ thống lõi tứ i7 (khi cửa sổ trình duyệt là cửa sổ duy nhất), đến nỗi tôi thấy mình ước mình có thể truy cập vào bộ đếm thời gian chính xác 0,1ms trong JavaScript, vì độ chính xác cuối cùng là bây giờ trên một số hệ thống duyệt web cao cấp để làm cho độ chính xác của bộ đếm thời gian như vậy trở nên đáng giá đối với một số loại ứng dụng thích hợp đòi hỏi độ chính xác cao và nơi các ứng dụng có thể tự xác minh độ lệch về độ chính xác.

Rõ ràng nếu bạn đang thực hiện nhiều đường chuyền, bạn có thể chỉ cần chạy nhiều đường chuyền (ví dụ: 10 đường chuyền) sau đó chia cho 10 để có độ chính xác 0,1 mili giây. Đó là phương pháp phổ biến để có được độ chính xác tốt hơn - thực hiện nhiều đường chuyền và chia tổng thời gian cho số đường chuyền.

TUY NHIÊN ... Nếu tôi chỉ có thể vượt qua một điểm chuẩn duy nhất của một bài kiểm tra cụ thể do một tình huống bất thường duy nhất, tôi phát hiện ra rằng tôi có thể nhận được độ chính xác 0,1 (Và đôi khi là 0,01ms) bằng cách thực hiện điều này:

Khởi tạo / Hiệu chỉnh:

  1. Chạy một vòng lặp bận để chờ cho đến khi bộ đếm thời gian tăng lên một phần nghìn giây tiếp theo (căn chỉnh bộ đếm thời gian để bắt đầu khoảng phần nghìn giây tiếp theo) Vòng lặp bận này kéo dài dưới một phần nghìn giây.
  2. Chạy một vòng lặp bận khác để tăng bộ đếm trong khi chờ bộ đếm thời gian tăng. Bộ đếm cho bạn biết có bao nhiêu gia số bộ đếm xảy ra trong một phần nghìn giây. Vòng lặp bận rộn này kéo dài một mili giây đầy đủ.
  3. Lặp lại các bước trên, cho đến khi các số trở nên cực kỳ ổn định (thời gian tải, trình biên dịch JIT, v.v.). 4. LƯU Ý: Tính ổn định của số mang lại cho bạn độ chính xác có thể đạt được trên hệ thống không hoạt động. Bạn có thể tính toán phương sai, nếu bạn cần tự kiểm tra độ chính xác. Sự khác biệt lớn hơn trên một số trình duyệt và nhỏ hơn trên các trình duyệt khác. Lớn hơn trên hệ thống nhanh hơn và chậm hơn trên hệ thống chậm hơn. Tính nhất quán cũng khác nhau. Bạn có thể biết trình duyệt nào nhất quán / chính xác hơn những trình duyệt khác. Hệ thống chậm hơn và hệ thống bận rộn sẽ dẫn đến sự khác biệt lớn hơn giữa các lần khởi tạo. Điều này có thể cho bạn cơ hội hiển thị thông báo cảnh báo nếu trình duyệt không cung cấp cho bạn đủ độ chính xác để cho phép đo 0,1ms hoặc 0,01ms. Độ lệch của bộ đếm thời gian có thể là một vấn đề, nhưng một số bộ đếm thời gian số nguyên mili giây trên một số hệ thống tăng khá chính xác (khá đúng trên dấu chấm), điều này sẽ dẫn đến các giá trị hiệu chuẩn rất nhất quán mà bạn có thể tin tưởng.
  4. Lưu giá trị bộ đếm cuối cùng (hoặc giá trị trung bình của một vài lần hiệu chuẩn gần nhất)

Đo điểm chuẩn một lần cho độ chính xác dưới mili giây:

  1. Chạy một vòng lặp bận để đợi cho đến khi bộ đếm thời gian tăng lên một phần nghìn giây tiếp theo (căn chỉnh bộ hẹn giờ để bắt đầu khoảng phần nghìn giây tiếp theo). Vòng lặp bận rộn này kéo dài ít hơn một phần nghìn giây.
  2. Thực hiện nhiệm vụ bạn muốn để chuẩn chính xác thời gian.
  3. Kiểm tra bộ đếm thời gian. Điều này cung cấp cho bạn số nguyên mili giây.
  4. Chạy một vòng lặp bận cuối cùng để tăng bộ đếm trong khi chờ bộ đếm thời gian tăng. Vòng lặp bận rộn này kéo dài ít hơn một phần nghìn giây.
  5. Chia giá trị bộ đếm này cho giá trị bộ đếm ban đầu khi khởi tạo.
  6. Bây giờ bạn đã có phần thập phân của mili giây !!!!!!!!

CẢNH BÁO: Vòng lặp bận KHÔNG được khuyến nghị trong trình duyệt web, nhưng may mắn thay, những vòng lặp bận này chạy dưới 1 mili giây mỗi vòng và chỉ được chạy một vài lần.

Các biến như biên dịch JIT và dao động CPU tạo thêm độ chính xác lớn, nhưng nếu bạn chạy một số lần khởi tạo, bạn sẽ có toàn bộ quá trình biên dịch động và cuối cùng bộ đếm chuyển thành một thứ gì đó rất chính xác. Đảm bảo rằng tất cả các vòng lặp bận đều có cùng chức năng đối với mọi trường hợp, để sự khác biệt về các vòng lặp bận không dẫn đến sự khác biệt. Đảm bảo rằng tất cả các dòng mã được thực thi nhiều lần trước khi bạn bắt đầu tin tưởng vào kết quả, để cho phép các trình biên dịch JIT đã ổn định ở chế độ biên dịch lại động đầy đủ (dynarec).

Trên thực tế, tôi đã chứng kiến ​​độ chính xác đạt đến từng micro giây trên một số hệ thống nhất định , nhưng tôi vẫn chưa tin tưởng điều đó. Nhưng độ chính xác 0,1 mili giây dường như hoạt động khá đáng tin cậy, trên một hệ thống lõi tứ không hoạt động, nơi tôi là trang trình duyệt duy nhất. Tôi đã đến với một trường hợp thử nghiệm khoa học nơi tôi chỉ có thể thực hiện một lần vượt qua (do các biến số duy nhất xảy ra) và cần tính thời gian chính xác cho mỗi lần vượt qua, thay vì lấy trung bình nhiều lần vượt qua, vì vậy đó là lý do tại sao tôi đã làm điều này.

Tôi đã thực hiện một số lần vượt qua trước và vượt qua giả (cũng để giải quyết sự cố), để xác minh độ tin cậy của độ chính xác 0,1ms (giữ vững trong vài giây), sau đó giữ tay khỏi bàn phím / chuột, trong khi điểm chuẩn xảy ra, sau đó thực hiện một số sau khi vượt qua để xác minh độ tin cậy của độ chính xác 0,1ms (vẫn vững chắc trở lại). Điều này cũng xác minh rằng những thứ chẳng hạn như thay đổi trạng thái quyền lực hoặc những thứ khác, không xảy ra giữa trước và sau, ảnh hưởng đến kết quả. Lặp lại bài kiểm tra trước và kiểm tra sau giữa mỗi lần vượt qua điểm chuẩn. Sau đó, tôi gần như chắc chắn rằng các kết quả ở giữa là chính xác. Tất nhiên, không có gì đảm bảo, nhưng nó cho thấy rằng có thể có độ chính xác <0,1ms trong một số trường hợp trong một trình duyệt web.

Phương pháp này chỉ hữu ích trong những trường hợp rất, rất thích hợp. Mặc dù vậy, nó thực sự sẽ không được đảm bảo vô hạn 100%, bạn có thể đạt được độ chính xác khá đáng tin cậy và thậm chí là độ chính xác khoa học khi kết hợp với một số lớp xác minh bên trong và bên ngoài.


3
Việc xác định thời gian với độ chính xác vượt trội trước đây rất phức tạp vì tất cả những gì chúng tôi có là Date.now()hoặc +new Date(). Nhưng bây giờ chúng tôi có performance.now(). Mặc dù rõ ràng là bạn đã tìm ra một số cách thú vị để tăng thêm khả năng, nhưng câu trả lời này về cơ bản đã lỗi thời. Ngoài ra, không đề xuất bất cứ điều gì liên quan đến vòng lặp bận. Đừng làm vậy. Chúng tôi không cần nhiều hơn thế nữa.
Steven Lu

1
Hầu hết các trình duyệt đã giảm độ chính xác của việc triển khai performance.now () để tạm thời giảm thiểu cuộc tấn công định thời gian bộ nhớ cache. Tôi tự hỏi liệu câu trả lời này có còn ý nghĩa trong nghiên cứu bảo mật hay không.
Qi Fan

2
Xem lại nhận xét của riêng tôi. Chà, tôi đã đăng phần trên vào năm 2012 rất lâu trước khi performance.now (). Nhưng bây giờ điều đó hơi mờ đi một chút bởi các giải pháp thay thế Meltdown / Spectre. Một số trình duyệt đã xuống cấp nghiêm trọng performance.now () vì lý do bảo mật. Tôi nghĩ rằng kỹ thuật trên có lẽ đã lấy lại được một số liên quan một lần nữa đối với rất nhiều trường hợp sử dụng điểm chuẩn hợp pháp, tùy thuộc vào các giới hạn của bộ đếm thời gian.
Mark Rejhon

3

Nói chung, câu trả lời là "không". Nếu bạn đang sử dụng JavaScript trong một số môi trường phía máy chủ (nghĩa là không phải trong trình duyệt), thì tất cả các cược sẽ tắt và bạn có thể thử làm bất cứ điều gì bạn muốn.

chỉnh sửa - câu trả lời này đã cũ; các tiêu chuẩn đã tiến bộ và các phương tiện mới hơn có sẵn như là giải pháp cho vấn đề thời gian chính xác. Mặc dù vậy, cần nhớ rằng bên ngoài miền của hệ điều hành thời gian thực thực sự, mã không đặc quyền thông thường có quyền kiểm soát hạn chế đối với quyền truy cập của nó để tính toán tài nguyên. Đo lường hiệu suất không giống (nhất thiết) như dự đoán hiệu suất.


2

Đây là một ví dụ hiển thị bộ hẹn giờ độ phân giải cao của tôi cho 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
 }

Sử dụng:

 const start = startTimer();

 console.log('test');

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

Thông thường, bạn có thể sử dụng:

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

Nếu bạn đang tính thời gian các phần mã liên quan đến vòng lặp, bạn không thể có quyền truy cập vào giá trị của console.timeEnd()để thêm các kết quả hẹn giờ của bạn lại với nhau. Bạn có thể làm được, nhưng nó trở nên khó chịu vì bạn phải nhập giá trị của biến lặp của mình, chẳng hạn như i, và đặt một điều kiện để phát hiện xem vòng lặp đã hoàn thành chưa.

Đây là một ví dụ vì nó có thể hữu ích:

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

Trích dẫn: https://nodejs.org/api/process.html#process_process_hrtime_time

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.