Точность rdtsc на ядрах процессора
Я отправляю сетевые пакеты из одного потока и получаю ответы во втором потоке, который работает на другом ядре ЦП. Мой процесс измеряет время между отправкой и получением каждого пакета (аналогично ping). Я использую rdtsc для получения синхронизации с высоким разрешением и минимальными издержками, которая необходима для моей реализации.
Все измерения выглядят надежно. Тем не менее, меня беспокоит точность rdtsc для всех ядер, так как я читал некоторые тексты, которые подразумевали, что tsc не синхронизируется между ядрами.
Я нашел следующую информацию о TSC в Википедии
Постоянное поведение TSC обеспечивает одинаковую длительность каждого тактового сигнала и поддерживает использование TSC в качестве настенного таймера, даже если ядро процессора меняет частоту. Это архитектурное поведение, продвигающееся для всех процессоров Intel.
Тем не менее меня беспокоит нарастание по всем ядрам, и это мой вопрос
Больше информации
- Я запускаю процесс на машине Intel Nehalem.
- Операционная система Linux.
- Флаг процессора " constant_tsc " устанавливается для всех ядер.
6 ответов
X86_FEATURE_CONSTANT_TSC
+ X86_FEATURE_NONSTOP_TSC
биты в процессоре (edx=x80000007, бит № 8; проверка unsynchronized_tsc
функция ядра Linux для дополнительных проверок)
Intel's Designer vol3b, раздел 16.11.1 Инвариантный TSC говорит следующее
"16.11.1 Инвариант TSC
Счетчик меток времени в более новых процессорах может поддерживать расширение, называемое инвариантным TSC. Поддержка процессором инвариантного TSC указана в CPUID.80000007H:EDX[8].
Инвариант TSC будет работать с постоянной скоростью во всех ACPI P-, C-. и Т-состояния. Это архитектурное поведение, движущееся вперед. На процессорах с инвариантной поддержкой TSC ОС может использовать TSC для служб таймера настенных часов (вместо таймеров ACPI или HPET). Чтения TSC намного более эффективны и не требуют дополнительных затрат, связанных с переходом по кольцу или доступом к ресурсу платформы ".
Таким образом, если TSC можно использовать для настенных часов, они гарантированно синхронизируются.
На последних процессорах вы можете делать это между отдельными ядрами одного и того же пакета (то есть системой с одним процессором iX), вы просто не можете делать это в отдельных пакетах (процессорах), потому что они не будут совместно использовать rtc. Вы можете избежать неприятностей с помощью привязки к процессору (привязывая соответствующие потоки к конкретным ядрам), но опять же это будет зависеть от поведения вашего приложения.
В Linux вы можете проверить constant_tsc в /proc/cpuinfo, чтобы увидеть, есть ли у процессора единственная tsc, действительная для всего пакета. Необработанный регистр находится в CPUID.80000007H:EDX[8]
То, что я прочитал, но еще не подтвердил программно, - то, что процессоры AMD начиная с версии 11h имеют то же значение для этого бита процессора.
На самом деле, кажется, что ядра не разделяют TSC, проверьте эту ветку: http://software.intel.com/en-us/forums/topic/388964
Подводя итог, можно сказать, что разные ядра не разделяют TSC, иногда TSC может выйти из синхронизации, если ядро переходит в конкретное состояние энергии, но это зависит от типа процессора, поэтому вам нужно проверить документацию Intel. Похоже, что большинство операционных систем синхронизируют TSC при загрузке.
Я проверил различия между TSC на разных ядрах, используя захватывающий алгоритм, на машине Linux Debian с процессором Core i5. Процесс возбуждения (в одном ядре) записал TSC в общую переменную, когда реагирующий процесс обнаружил изменение в этой переменной, он сравнивает ее значение и сравнивает его со своим собственным TSC. Это пример вывода моей тестовой программы:
TSC ping-pong test result:
TSC cores (exciter-reactor): 0-1
100 records, avrg: 159, range: 105-269
Dispersion: 13
TSC ping-pong test result:
TSC cores (exciter-reactor): 1-0
100 records, avrg: 167, range: 125-410
Dispersion: 13
Время реакции, когда ЦП возбудителя равен 0 (в среднем 159 тиков), почти такое же, как когда ЦП возбудителя равен 1 (167 тиков). Это указывает на то, что они довольно хорошо синхронизированы (возможно, с некоторыми отличиями). На других парах ядер результаты были очень похожи.
С другой стороны, инструкция сборки rdtscp возвращает значение, указывающее ЦП, в котором был прочитан TSC. Это не ваш случай, но он может быть полезен, когда вы хотите измерить время в простом сегменте кода и хотите, чтобы процесс не был перемещен ЦП в середине кода.
В linux вы можете использовать clock_gettime(3) с CLOCK_MONOTONIC_RAW, что дает вам перенесение на наносекунды и не требует обновления ntp (если таковое произошло).
Я рекомендую вам не использовать rdtsc. Мало того, что он не переносимый, он не надежен и, как правило, не будет работать - на некоторых системах rdtsc не обновляется равномерно (например, если вы используете speedstep и т. Д.). Если вам нужна точная информация о времени, вы должны установить опцию SO_TIMESTAMP для сокета и использовать recvmsg(), чтобы получить сообщение с меткой времени (микросекундное разрешение).
Более того, временная метка, которую вы получаете с SO_TIMESTAMP, фактически соответствует времени, когда ядро получило пакет, а не когда ваша задача заметила это.
Вы можете установить сходство потоков, используя sched_set_affinity()
API для запуска потока на одном ядре процессора.