Как вызвать две функции одну за другой с минимальной задержкой?

#c #linux #time #system-calls #rdtsc

#c #linux #время #системные вызовы #rdtsc

Вопрос:

Я реализовал быструю функцию, которая возвращает время (используя rdtsc), давайте назовем ее fast_time() . У меня есть для справки исходная функция, которая использует системный вызов, давайте назовем ее system_time() . Моя программа использует fast_time() , но в отдельном потоке я постоянно запускаю цикл while, чтобы проверить, превышает ли разница между временем, возвращаемым моей функцией, и исходной функцией заданный порог. Что-то вроде

 while (true)
{
    if (abs((fast_time() - system_time()).microseconds()) > MICROSECONDS_THRESHOLD)
    {
        report error
    }
}
  

Обычно я не вижу ошибок, но иногда я их получаю, и я хотел бы лучше разобраться в этой проблеме. Мое первое подозрение заключается в том, что вызов system_time() не происходит сразу после fast_time() возврата. Как я могу заставить программу выполняться fast_time() - system_time() по возможности «атомарно»?

Комментарии:

1. Привет, прежде чем я отвечу, ваш код привязан к одному процессору? Ничего страшного, если вы не знаете, что это значит, просто скажите мне, с чего начать объяснение 🙂

2. Если вы не работаете на ядре реального времени (а Linux таковым не является), вы не получаете никаких гарантий того, как часто планируется запуск вашего программного обеспечения, и вы не можете знать, будут ли потоки выполняться на одном ядре по очереди или на разных ядрах. Обычно это полностью зависит от операционной системы.

3. @LtWorf на самом деле в Linux есть планировщик реального времени, если вы решите его использовать, и в его конкретном случае на многоядерном процессоре он может привязать свой поток к ЯДРУ и получить результаты, которые он ищет.

4. Я привязываю поток, который к одному ядру с помощью sched_setaffinity , и да, я знаю, что я не получаю гарантий для планирования (я не использую ядро реального времени), мой вопрос в том, что это лучшее, что я могу сделать, чтобы исправить это?

5. Чего вы хотите достичь с помощью теста? Является ли это тестом для fast_time реализации, которую вы сравниваете с той system_time , которой вы доверяете?

Ответ №1:

rdtsc по своей сути является ненадежным способом измерения в реальном времени по нескольким причинам:

1) каждый поток HW будет возвращать разные значения, поэтому, если ваш поток не привязан к одному потоку HW, вы получите разные значения. Таким образом, время может неожиданно прыгать вперед и назад в зависимости от того, как ОС планирует поток

И что еще более важно:

2) Регулирование процессора может привести к неожиданному изменению частоты процессора. Поскольку rdtsc измеряет выполненные циклы, а не время, вы не можете надежно получить прошедшее время, разделив количество циклов на частоту процессора (между двумя вызовами rdtsc частота могла измениться случайным образом)

Также в вашем конкретном тестовом примере ОС может запланировать поток для выхода между вызовами fast_time() и system_time(), что вызывает расхождение между измеренными временами, даже если fast_time () вернет правильные значения. Итак, отвечая на ваш вопрос, я не думаю, что есть какой-либо способ предотвратить это.

Комментарии:

1. Вы правы! Если проблема не в планировании, то мое следующее предположение заключается в том, что частота процессора меняется (разгоняется или не разгоняется). В любом случае я не думаю, что готов так легко отказаться от rdtsc, поскольку он оказался в 4 раза быстрее системного вызова. Итак, как вы упомянули, я подозреваю, что ОС выдает мой поток именно тогда, когда я не хочу, чтобы он планировал мой поток. Итак, я все еще ищу какой-нибудь способ предотвратить это

2. Может быть, вы могли бы поместить system_time () между двумя вызовами fast_time() и отклонить случай, если время между вызовами fast_time() превышает некоторый порог? Мне также любопытно, в каком конкретном приложении разница во времени вызова в 4 раза имеет значение?

3. Отвечая на ваш второй вопрос, приложение относится к области алгоритмической торговли, где важна каждая микросекунда.

4. Я не знаю, какую платформу вы используете, но я профилировал QueryPerformanceCounter() против __rdtsc() в win32, и для 1 миллиарда вызовов я получил время 10,63 с против 8,11 с соответственно. Так что, по крайней мере, в этом случае не стоит лишних хлопот использовать rdtsc.