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

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

2 ответа

Решение

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

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

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

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

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

Идея не лишена смысла, но она не подходит для приложений пользовательского режима, для которых, как предположил @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, Вы можете игнорировать K, так как вас интересуют только дельты. От ART_Value дельта вы можете получить время, когда вы знаете частоту АРТ. Это дается как k * B, где k является константой в MSR MSR_PLATFORM_INFO и B составляет 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.

Другие вопросы по тегам