Может ли Clock с прямым значением QueryPerformanceCounter соответствовать стандарту C ++? - PullRequest
2 голосов
/ 27 марта 2020

Если я хочу создать Clock с прямым QueryPerformanceCounter Windows API результатом. QueryPerformanceCounter Windows API возвращает некоторый счетчик, который должен быть разделен на результат QueryPerformanceFrequency, что дает время в секундах.

Обычно Clock на основе QueryPerformanceCounter немедленно преобразует результат в некоторые единицы путем умножения на некоторый период и делится на QueryPerformanceFrequency. Вот как steady_clock может быть реализовано на Windows.

Но предположим, что из соображений производительности я хочу избегать деления до тех пор, пока оно действительно не понадобится. Таким образом, time_point является прямым QueryPerformanceCounter значением, а duration является разницей такого. И я могу выполнять арифметику c и сравнение с этими значениями большую часть времени, преобразовывая в обычный duration или time_point только конечный результат.

Я уверен, что это возможно. У меня вопрос: будут ли такие Clock полностью совместимы со стандартом Clock.

Ответы [ 2 ]

2 голосов
/ 27 марта 2020

Это не будет полностью совместимо со стандартом Требования к часам . Но он будет компилироваться и делать то, что вы хотите, большую часть времени. Часть, которая не соответствует, состоит в том, что вам придется указать что-то для period, на котором основан ваш time_point. И это что-то не обязательно будет соответствовать физическим единицам времени.

Это не будет иметь значения, пока вы не вычтете две из этих точек времени, не получите duration, а затем не сравните это duration с чем-то, что представляет физическое время. Тогда вы получите мусор во время выполнения.

Также, если вы используете такие time_point in sleep_until или wait_until, то ваша программа не будет спать или ждать ожидаемое время.

Вот пример хронографа на основе QueryPerformanceCounter, который прибивает физические единицы к QueryPerformanceFrequency: { ссылка }

0 голосов
/ 28 марта 2020

Часы с переопределенными time_point и duration, которые не соответствуют period, известным во время компиляции, не соответствуют Clock требованиям, как объяснил Говард Хиннант.


Использование класс, эмулирующий арифметику c типа для скрытия дополнительных деталей реализации, не работает.

В стандарте не определено, что это за эмуляция , возможность использования эмуляции Тип доступен только для часов, предусмотренных стандартной реализацией. В качестве практического доказательства std::chrono и boost::chrono не работают хорошо с boost::rational.


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

Первое решение состоит в том, чтобы следовать намерению отложить деление до необходимого, но не переопределять time_point и duration. Определите стандартные часы и, как расширение, определите интерфейс для необработанной метки времени. Таким образом, часы могут иметь метод raw_now(), возвращающий тип raw_time_point, разница такого типа raw_duration. raw_time_point может быть неявно преобразовано в time_point и raw_duration в duration.


Вторым решением является вычисление стандартного time_point для каждого запроса времени, но используйте libdivide библиотека, которая выполняет быстрое деление без операции деления.

Библиотека берет делитель и обрабатывает его так, что каждое деление с использованием обработанного делителя выполняется с использованием быстрых операций. Конечно, для обработки делителя требуется время, но для повторного деления с одним и тем же делителем это одноразовые накладные расходы. Так что эта библиотека выглядит идеально подходящей для этой задачи. (Обратите внимание, что библиотека бесполезна для констант, известных во время компиляции, так как в этом случае компилятор избегает деления сам по себе, но QueryPerformanceFrequency результатом является постоянная времени выполнения).

...