Я экспериментировал со всеми видами таймеров в Linux и OSX и хотел бы попытаться обернуть некоторые из них тем же интерфейсом, что и std :: chrono.
Это легко сделать для таймеров, которые имеют четко определенный «период» во время компиляции, например, семейство POSIX clock_gettime (), семейство clock_get_time () в OSX или gettimeofday ().
Однако есть некоторые полезные таймеры, для которых «период» — хотя и постоянный — известен только во время выполнения.
Например:
— POSIX сообщает, что период clock (), CLOCKS_PER_SEC, может быть переменной в не-XSI системах
— в Linux период times () задается во время выполнения sysconf (_SC_CLK_TCK)
— в OSX период mach_absolute_time () задается во время выполнения посредством mach_timebase_info ()
— на последних процессорах Intel регистр DST работает с постоянной скоростью, но, конечно, это можно определить только во время выполнения
Чтобы обернуть эти таймеры в интерфейс std :: chrono, одной из возможностей будет использование периода std :: chrono :: наносекунда и преобразование значения каждого таймера в наносекунды. Другим подходом может быть использование представления с плавающей запятой. Однако оба подхода привели бы к (очень небольшим) издержкам функции now () и (возможно, небольшим) потерям в точности.
Решение, которое я пытаюсь найти, состоит в том, чтобы определить набор классов для представления таких «постоянных времени выполнения» периодов, построенных по тем же линиям, что и класс std :: ratio.
Однако я ожидаю, что для этого потребуется переписать все связанные классы и функции шаблона (так как они принимают значения constexpr).
У кого-нибудь есть опыт работы с такими таймерами в стиле хронографа?
Или с использованием значений non-constexpr для периода времени часов?
Кто-нибудь имеет опыт работы с такими таймерами?
la std: хроно?
На самом деле я делаю. И на OSX, одна из ваших платформ интереса. 🙂
Вы упоминаете:
в OSX период mach_absolute_time () задается во время выполнения как
mach_timebase_info ()
Абсолютно правильно. Также на OSX Libc ++ реализация high_resolution_clock
а также steady_clock
на самом деле основан на mach_absolute_time
, Я являюсь автором этого кода с открытым исходным кодом и щедрой лицензией (делайте с ним все, что хотите, пока сохраняете авторские права).
Вот источник для Libc ++ s steady_clock::now()
. Он построен так, как вы и предполагали. Время выполнения преобразуется в наносекунды до возвращения. В OS X коэффициент преобразования очень часто равен 1, и код использует этот факт для оптимизации. Однако код достаточно общий для обработки не-1 коэффициентов преобразования.
По первому звонку now()
есть небольшая стоимость запроса коэффициента преобразования времени выполнения в наносекунды. В общем случае вычисляется коэффициент преобразования с плавающей запятой. В общем случае (коэффициент преобразования == 1) последующая стоимость вызывается через указатель функции. Я обнаружил, что накладные расходы действительно вполне разумны.
В OS X коэффициент преобразования, хотя и не определяется до времени выполнения, остается постоянным (т. Е. Не изменяется во время выполнения программы), поэтому его необходимо вычислять только один раз.
Если вы находитесь в ситуации, когда ваш период на самом деле меняется динамически, вам понадобится больше инфраструктуры, чтобы справиться с этим. По сути, вам нужно будет интегрировать (исчислить) период и кривую времени, а затем вычислить средний период между двумя моментами времени. Это потребует постоянного мониторинга периода, так как он меняется со временем, и <chrono>
не правильный инструмент для этого. Такие инструменты обычно обрабатываются на уровне ОС.
[У кого-нибудь есть опыт] Или с использованием значений non-constexpr для периода времени часов?
После прочтения стандарта (20.11.5, Продолжительность шаблона класса) «период», как ожидается, будет «специализация отношения»:
Примечания: Если Период не является специализацией соотношения, программа некорректна.
и все шаблоны хроно в значительной степени зависят от функциональности constexpr.
У кого-нибудь есть опыт работы с такими таймерами в стиле хронографа?
я обнаружил Вот предложение использовать длительность с period = 1, boost :: рациональное в качестве rep, но без каких-либо конкретных примеров.
Я сделал подобное для моих целей, но только для Linux. Вы найдете код Вот; не стесняйтесь использовать код любым удобным для вас способом.
Проблемы, с которыми сталкивается моя реализация, частично совпадают с теми, которые упоминаются в вашем вопросе. В частности:
Тиковый коэффициент (необходимый для преобразования тиков часов в единицу времени на основе секунд) извлекается во время выполнения, но только в первый раз now()
используется‡. Если вас беспокоят небольшие накладные расходы, вы можете позвонить now()
Работайте один раз при запуске, прежде чем измерять фактические интервалы. Фактор тика сохраняется в статической переменной, что означает, что все еще есть некоторые издержки, так как — на самом низком уровне — каждый вызов now()
Функция подразумевает проверку инициализации статической переменной. Тем не менее, эти накладные расходы будут то же самое в каждом вызове now()
, так что это не должно влиять на измерения временных интервалов.
По умолчанию я не преобразую в наносекунды, потому что при измерении относительно длинных периодов времени (например, нескольких секунд) это вызывает очень быстрое переполнение. На самом деле это главная причина, почему я не использую реализацию boost. Вместо преобразования в наносекунды, я реализую базовый блок как параметр шаблона (называемый Precision
в коде). я использую std::ratio
из C ++ 11 в качестве аргументов шаблона. Так что я могу выбрать, например, clock<micro>
, что подразумевает, что вызов now()
Функция будет внутренне преобразовываться в микросекунды, а не наносекунды, что означает, что я могу измерять периоды в несколько секунд или минут без переполнений и при этом с хорошей точностью. (Это не зависит от единицы, используемой для производства продукции. Вы можете иметь clock<micro>
и отобразить результат в секундах и т. д.)
Мой тип часов, который называется combined_clock
объединяет пользовательское время, системное время и время настенных часов. Для этого тоже есть тип тактовых импульсов, но он не совместим с типами коэффициентов и единицами измерения из std
тогда как мой есть.
‡Тиковый коэффициент определяется с помощью ::sysconf()
Позвоните, вы предлагаете, и это гарантированно вернуть одно и то же значение в течение всего срока службы процесса.
Таким образом, вы используете его следующим образом:
#include "util/proctime.hpp"
#include <ratio>
#include <chrono>
#include <thread>
#include <utility>
#include <iostream>
int main()
{
using std::chrono::duration_cast;
using millisec = std::chrono::milliseconds;
using clock_type = rlxutil::combined_clock<std::micro>;
auto tp1 = clock_type::now();
/* Perform some random calculations. */
unsigned long step1 = 1;
unsigned long step2 = 1;
for (int i = 0 ; i < 50000000 ; ++i) {
unsigned long step3 = step1 + step2;
std::swap(step1,step2);
std::swap(step2,step3);
}
/* Sleep for a while (this adds to real time, but not CPU time). */
std::this_thread::sleep_for(millisec(1000));
auto tp2 = clock_type::now();
std::cout << "Elapsed time: "<< duration_cast<millisec>(tp2 - tp1)
<< std::endl;
return 0;
}
Приведенное выше использование включает в себя функцию pretty-print, которая генерирует вывод, подобный следующему:
Elapsed time: [user 40, system 0, real 1070 millisec]