Рассчитать системное время, используя rdtsc

Предположим, что все ядра в моем процессоре имеют одинаковую частоту, технически я могу синхронизировать пары счетчиков системного времени и меток времени для каждого ядра каждую миллисекунду или около того. Затем, основываясь на текущем ядре, с которым я работаю, я могу взять текущий rdtsc и используя тиковую дельту, деленную на частоту ядра, я могу оценить время, прошедшее с момента последней синхронизации пары счетчиков системного времени и метки времени, и вывести текущее системное время без издержек на системный вызов из моего текущего потока ( при условии, что для извлечения вышеуказанных данных блокировки не нужны).
Это прекрасно работает в теории, но на практике я обнаружил, что иногда я получаю больше тиков, чем я ожидал, то есть, если бы моя частота ядра составляла 1 ГГц, и я использовал пару счетчиков системного времени и метки времени 1 миллисекунду назад, я ожидал увидеть дельту в тиках, который составляет около 10 ^ 6 тиков, но на самом деле я обнаружил, что это может быть где-то между 10 ^ 6 и 10 ^ 7.
Я не уверен, что не так, может кто-нибудь поделиться своими мыслями о том, как рассчитать системное время, используя rdtsc? Моя главная цель — избежать необходимости выполнять системный вызов каждый раз, когда я хочу знать системное время и иметь возможность выполнять вычисления в пространстве пользователя, которые дадут мне его хорошую оценку (в настоящее время я определяю хорошую оценку в результате с интервалом в 10 микросекунд от реального системного времени.

3

Решение

Не делайте этого, используя себя непосредственно RDTSC машинная инструкция — (потому что ваш планировщик ОС может перепланировать другие потоки или процессы в произвольные моменты или замедлить время). Используйте функцию, предоставляемую вашей библиотекой или ОС.

Моя главная цель — избежать необходимости выполнять системный вызов каждый раз, когда я хочу узнать системное время

В Linux читайте Время (7) затем используйте clock_gettime (2) что очень быстро (и не требует каких-либо медленных системный вызов) благодаря vdso (7).

В C ++ 11-совместимой реализации просто используйте стандарт <chrono> заголовок. И стандарт С имеет Часы (3) (дает микросекундную точность). Оба будут использовать в Linux достаточно хорошие функции измерения времени (так косвенно vdso)

В прошлый раз я измерял clock_gettime это часто занимало менее 4 наносекунд за звонок.

7

Другие решения

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

Сама Intel предлагает использовать TSC в качестве настенных часов:

Инвариант TSC будет работать с постоянной скоростью во всех ACPI P-, C-. и Т-состояния.
Это архитектурное поведение
Движение вперед. На процессорах с инвариантной поддержкой TSC ОС может использовать TSC для служб таймера
(вместо таймеров ACPI или HPET). Чтения TSC намного более эффективны и не несут накладных расходов, связанных с
кольцевой переход или доступ к ресурсу платформы.

Тем не менее, нужно быть осторожным.

TSC не всегда инвариантен

В старых процессорах TSC увеличивается на каждом внутренний Цикл часов, это были не настенные часы.
Цитата Intel

Для процессоров Pentium M (семейство [06H], модели [09H, 0DH]); для процессоров Pentium 4, процессоров Intel Xeon
(семейство [0FH], модели [00H, 01H или 02H]); и для процессоров семейства P6: увеличение счетчика меток времени
с каждым тактом внутреннего процессора.

Внутренний тактовый такт процессора определяется текущим отношением частоты ядра к частоте шины. Intel®
Переходы технологии SpeedStep® также могут влиять на тактовую частоту процессора.

Если у вас есть только вариант TSC, измерения ненадежны для отслеживания времени.
Хотя есть надежда на инвариантный TSC.

TSC не увеличивается с частотой, указанной в строке бренда

Все еще цитирую Intel

счетчик отметок времени увеличивается с постоянной скоростью. Этот показатель может быть установлен
максимальное отношение тактовой частоты ядра к тактовой частоте процессора или может быть установлено максимальной разрешенной частотой в
какой процессор загружается. Максимальная разрешенная частота может отличаться от базы процессора
частота.
На некоторых процессорах частота TSC может не совпадать
как частота в строке бренда.

Вы не можете просто взять частоту, написанную на коробке процессора.
Увидеть ниже.

rdtsc не сериализуется

Вам нужно сериализовать его сверху и снизу.
Увидеть этот.

TSC основан на ART (всегда работающем таймере), когда инвариант

Правильная формула

TSC_Value = (ART_Value * CPUID.15H:EBX[31:0] )/ CPUID.15H:EAX[31:0] + K

См. Раздел 17.15.4 руководства Intel 3.

Конечно, вы должны решить для ART_Value так как вы начинаете с TSC_Value, Вы можете игнорировать К так как вас интересуют только дельты.
От ART_Value дельта вы можете получить время, когда вы знаете частоту АРТ. Это дано как К * В где К константа в MSR MSR_PLATFORM_INFO а также В 100 МГц или 133 + 1/3 МГц в зависимости от процессора.

Как @BeeOnRope отметил, что из Skylake частота кристалла ART больше не является частотой шины.
Фактические значения, поддерживаемые Intel, можно найти в файле turbostat.c.

switch(model)
{
case INTEL_FAM6_SKYLAKE_MOBILE: /* SKL */
case INTEL_FAM6_SKYLAKE_DESKTOP:    /* SKL */
case INTEL_FAM6_KABYLAKE_MOBILE:    /* KBL */
case INTEL_FAM6_KABYLAKE_DESKTOP:   /* KBL */
crystal_hz = 24000000;  /* 24.0 MHz */
break;
case INTEL_FAM6_SKYLAKE_X:  /* SKX */
case INTEL_FAM6_ATOM_DENVERTON: /* DNV */
crystal_hz = 25000000;  /* 25.0 MHz */
break;
case INTEL_FAM6_ATOM_GOLDMONT:  /* BXT */
crystal_hz = 19200000;  /* 19.2 MHz */
break;
default:
crystal_hz = 0;
}

TSC не увеличивается, когда процессор входит в глубокий сон

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

Переключение контекста отравит измерения

Там ничего не поделаешь.
Это фактически мешает вам вести хронометраж с TSC.

7

По вопросам рекламы [email protected]