Рассчитать системное время, используя 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.