Микросекундная синхронизация в JavaScript

Существуют ли какие-либо функции синхронизации в JavaScript с разрешением микросекунды?

Я знаю timer.js для Chrome, и надеюсь, что будет решение для других дружественных браузеров, таких как Firefox, Safari, Opera, Epiphany, Konqueror и т.д. Я не заинтересован в поддержке IE, но ответы, включая IE, приветствуются.

(Учитывая низкую точность миллисекундного времени в JS, я не задерживаю дыхание!)

Обновление: timer.js рекламирует разрешение в микросекундах, но просто умножает миллисекунду на 1000. Проверяется путем проверки и проверки кода. Разочарованный.: [

Ответ 1

Как упоминается в ответе Марка Рейхона, в современных браузерах есть API, который предоставляет данные синхронизации времени до миллисекунд для script: W3C High Таймер разрешения, aka window.performance.now().

now() лучше традиционного Date.getTime() двумя важными способами:

  • now() является двойным с субмиллисмодульным разрешением, которое представляет собой число миллисекунд с момента начала навигации по странице. Он возвращает количество микросекунд в дробном (например, значение 1000.123 составляет 1 секунду и 123 микросекунды).

  • now() монотонно возрастает. Это важно, поскольку Date.getTime() может прыгать вперед или даже назад при последующих вызовах. Примечательно, что если обновляется системное время ОС (например, синхронизация атомных часов), также обновляется Date.getTime(). now() гарантированно будет монотонно возрастать, поэтому на него не влияет время системы ОС - всегда будет время настенных часов (если ваши настенные часы не являются атомарными...).

now() может использоваться почти в каждом месте, где new Date.getTime(), + new Date и Date.now(). Исключением является то, что Date и now() раз не смешиваются, поскольку Date основан на unix-epoch (количество миллисекунды с 1970 года), а now() - количество миллисекунд с момента начала навигации по вашей странице (поэтому оно будет намного меньше Date).

now() поддерживается в Chrome stable, Firefox 15+ и IE10. Существует также несколько полиполков.

Ответ 2

Теперь появился новый метод измерения микросекунд в javascript: http://gent.ilcore.com/2012/06/better-timer-for-javascript.html

Однако в прошлом я нашел грубый метод получения 0,1 миллисекундной точности в JavaScript из миллисекундного таймера. Невозможно? Неа. Продолжайте читать:

Я делаю эксперименты с высокой степенью точности, которые требуют самоконтролируемой точности таймера, и я обнаружил, что смог надежно получить точность 0,1 миллисекунды с некоторыми браузерами в определенных системах.

Я обнаружил, что в современных ускорителях с ускорением GPU на быстрых системах (например, i7 quad core, где несколько ядер простаивают, только окно браузера) - теперь я могу доверять таймерам с точностью до миллисекунды. Фактически, он стал настолько точным в незанятой системе i7, что я смог надежно получить то же миллисекунду более чем на 1000 попыток. Только когда я пытаюсь сделать что-то вроде загрузки дополнительной веб-страницы или другой, ухудшается точность в миллисекундах (И я могу успешно поймать свою деградированную точность, выполнив проверку до и после времени, чтобы увидеть, мое время обработки внезапно увеличилось до 1 или более миллисекунд - это помогает мне недействить результаты, которые, вероятно, слишком сильно пострадали от колебаний ЦП).

Он стал настолько точным в некоторых ускоренных браузерах с графическим процессором на четырехъядерных системах i7 (когда окно браузера является единственным окном), которое я нашел, мне хотелось бы получить доступ к таймеру точности 0,1 мс в JavaScript, поскольку точность наконец, появилась в некоторых высокопроизводительных системах просмотра, чтобы сделать такую ​​точность таймера целесообразной для определенных типов нишевых приложений, требующих высокой точности, и где приложения могут самостоятельно проверять отклонения точности.

Очевидно, что если вы выполняете несколько проходов, вы можете просто запустить несколько проходов (например, 10 проходов), а затем разделить на 10, чтобы получить точность 0,1 миллисекунды. Это общий метод получения лучшей точности - выполнить несколько проходов и делить общее время на количество проходов.

ОДНАКО... Если я могу сделать только один контрольный проход конкретного теста из-за необычно уникальной ситуации, я узнал, что могу получить точность 0,1 (а иногда и 0,01 мс), выполнив следующие действия:

Инициализация/Калибровка:

  • Запустите цикл занятости, чтобы подождать, пока таймер не увеличится до следующей миллисекунды (выровнять таймер до начала следующего миллисекундного интервала). Этот цикл занятости длится меньше миллисекунды.
  • Запустите еще один цикл занятости, чтобы увеличить счетчик при ожидании увеличения таймера. Счетчик сообщает вам, сколько встречных приращений произошло за одну миллисекунду. Этот цикл занятости длится одну миллисекунду.
  • Повторите вышеуказанное, пока номера не станут ультрастабильными (время загрузки, компилятор JIT и т.д.). 4. ПРИМЕЧАНИЕ. Стабильность номера дает вам достижимую точность на незанятой системе. Вы можете рассчитать дисперсию, если вам нужно самостоятельно проверить точность. Отклонения больше в некоторых браузерах и меньше в других браузерах. Больше на более быстрые системы и медленнее на более медленных системах. Консистенция тоже меняется. Вы можете определить, какие браузеры более согласованы/точны, чем другие. Более медленные системы и занятые системы приведут к большим различиям между проходами инициализации. Это может дать вам возможность отображать предупреждающее сообщение, если браузер не дает вам достаточной точности для измерения 0,1 мс или 0,01 мс. Сбой таймера может быть проблемой, но некоторые целые миллисекундные таймеры на некоторых системах растут довольно точно (совершенно верно на точке), что приведет к очень стабильным значениям калибровки, которым вы можете доверять.
  • Сохранить окончательное значение счетчика (или среднее значение последних проходов калибровки)

Сравнивая один проход с точностью до миллисекунды:

  • Запустите цикл занятости, чтобы подождать, пока таймер не увеличится до следующей миллисекунды (выровняйте таймер до начала следующего миллисекундного интервала). Этот цикл занятости длится менее миллисекунды.
  • Выполните задачу, для которой вы хотите точно определить время.
  • Проверьте таймер. Это дает вам целые миллисекунды.
  • Запустите последний цикл занятости, чтобы увеличить счетчик, ожидая увеличения таймера. Этот цикл занятости длится менее миллисекунды.
  • Разделите значение этого счетчика на исходное значение счетчика от инициализации.
  • Теперь вы получили десятичную часть миллисекунд!!!!!!!!

ПРЕДУПРЕЖДЕНИЕ. Занятые циклы НЕ рекомендуются в веб-браузерах, но, к счастью, эти циклы занятости работают менее 1 миллисекунды каждый и выполняются только несколько раз.

Переменные, такие как компиляция JIT и флуктуации ЦП, добавляют огромные неточности, но если вы запустите несколько проходов инициализации, у вас будет полная динамическая перекомпиляция, и, в конечном счете, счетчик окунется в нечто очень точное. Убедитесь, что все циклы занятости - это точно такая же функция для всех случаев, так что различия в циклах занятости не приводят к различиям. Убедитесь, что все строки кода выполняются несколько раз, прежде чем вы начнете доверять результатам, чтобы компиляторы JIT уже стабилизировались до полной динамической перекомпиляции (dynarec).

Фактически, я был свидетелем точности приближающихся микросекунд на некоторых системах, но я бы до сих пор не доверял этому. Но точность 0,1 миллисекунды, похоже, работает достаточно надежно, в простой четырехъядерной системе, где я - единственная страница браузера. Я пришел в научный тестовый пример, где я мог делать только одноразовые проходы (из-за уникальных переменных) и требовался точно для каждого прохода, а не для усреднения нескольких повторных проходов, поэтому я сделал это.

Я сделал несколько предварительных проходов и фиктивных проходов (также для расчета dynarec), чтобы проверить надежность точности 0,1 мс (оставался твердым в течение нескольких секунд), а затем держал руки от клавиатуры/мыши, затем выполнил несколько постпроходов, чтобы проверить надежность точности 0,1 мс (остался твердым снова). Это также проверяет, что такие вещи, как изменение состояния питания или другие вещи, не возникали между до и после, мешая результатам. Повторите предварительный тест и пост-тест между каждым пропуском теста. После этого я был практически уверен, что результаты между ними были точными. Разумеется, нет никакой гарантии, но это показывает, что в некоторых случаях в веб-браузере возможна точность точности < 0.1ms.

Этот метод полезен только в очень, очень нишевых случаях. Тем не менее, это буквально не будет на 100% бесконечно гарантированным, вы можете получить очень надежную точность и даже научную точность в сочетании с несколькими уровнями внутренних и внешних проверок.

Ответ 3

Ответ "нет", в общем. Если вы используете JavaScript в некоторой серверной среде (то есть не в браузере), тогда все ставки отключены, и вы можете попытаться сделать все, что хотите.

edit — этот ответ старый; стандарты прогрессировали и появились новые возможности в качестве решения проблемы точного времени. Тем не менее, следует помнить, что за пределами области реальной операционной системы реального времени обычный непривилегированный код имеет ограниченный контроль над доступом к вычислительным ресурсам. Измерение производительности не является одинаковым (обязательно), как прогнозирование производительности.

Ответ 4

Вот пример, показывающий мой таймер с высоким разрешением для 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
 }

Использование:

 const start = startTimer();

 console.log('test');

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

Обычно вы можете использовать:

 console.time('Time since start');

 console.log('test');

 console.timeEnd('Time since start');

Если вы определяете разделы кода, которые связаны с циклом, вы не можете получить доступ к значению console.timeEnd(), чтобы добавить результаты таймера вместе. Вы можете, но это становится неприятным, потому что вам нужно ввести значение вашей итерирующей переменной, например i, и установить условие для определения того, завершен ли цикл.

Вот пример, потому что это может быть полезно:

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

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