2016-07-07 3 views
2

Я оцениваю производительность цикла ожидания занятости для запуска событий с постоянными интервалами. Я заметил, некоторые странное поведение, используя следующий код:Переменная производительность цикла ожидания ожидания?

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <time.h> 

int timespec_subtract(struct timespec *, struct timespec, struct timespec); 

int main(int argc, char *argv[]) { 
    int iterations = atoi(argv[1])+1; 

    struct timespec t[2], diff; 

    for (int i = 0; i < iterations; i++) { 
     clock_gettime(CLOCK_MONOTONIC, &t[0]); 

     static volatile int i; 
     for (i = 0; i < 200000; i++) 
      ; 

     clock_gettime(CLOCK_MONOTONIC, &t[1]); 

     timespec_subtract(&diff, t[1], t[0]); 
     printf("%ld\n", diff.tv_sec * 1000000000 + diff.tv_nsec); 
    } 
} 

На тестовой машине (двойной 14-жильный E5-2683 v3 @ 2.00Ghz, 256GB DDR4), 200k итераций для цикла составляет около 1 мс. Или, может быть, не так:

1030854 
1060237 
1012797 
1011479 
1025307 
1017299 
1011001 
1038725 
1017361 
... (about 700 lines later) 
638466 
638546 
638446 
640422 
638468 
638457 
638468 
638398 
638493 
640242 
... (about 200 lines later) 
606460 
607013 
606449 
608813 
606542 
606484 
606990 
606436 
606491 
606466 
... (about 3000 lines later) 
404367 
404307 
404309 
404306 
404270 
404370 
404280 
404395 
404342 
406005 

Когда раз смещаться вниз в третий раз, они остаются в основном соответствует (в течение примерно 2 или 3 мкс), за время от времени подпрыгивая до 450us за несколько сотен итераций, за исключением. Такое поведение повторяется на аналогичных машинах и во многих прогонах.

Я понимаю, что компилятор оптимизирован для циклов занятости, но я не думаю, что это проблема здесь. Я не думаю, что кэш должен влиять на него, потому что не должно быть никаких недействительных, и не будет объяснять внезапную оптимизацию. Я также попытался использовать регистр int для счетчика циклов без заметного эффекта.

Любые мысли о том, что происходит, и как сделать это (более) последовательным?

РЕДАКТИРОВАТЬ: Для получения информации, запуск этой программы с помощью usleep, nanosleep или показанного оживленного ожидания для 10k итераций показывает ~ 20000 непроизвольных переключателей контекста с time -v.

+0

Разрывы, вероятно, переключение контекста. – Mysticial

+2

Извините, но ваш подход совершенно неправ. Таким образом, вы не можете получить истинно надежное время в такой же системе. Это, безусловно, XY-проблема. Пожалуйста, укажите, что вы ** на самом деле ** хотите выполнить, и все соответствующие детали. – Olaf

+0

На самом деле я хочу понять, почему производительность цикла занятости изменяется, как я уже сказал в заголовке вопроса. Я знаю об альтернативных методах выбора времени моей программы. – Rakurai

ответ

1

Одна из больших проблем с оживленным занятием заключается в том, что, помимо использования ресурсов ЦП, количество ожидаемого времени будет сильно зависеть от скорости блока ЦП. Таким образом, один и тот же цикл может работать в разные времена на разных машинах.

Проблема с любым методом сна заключается в том, что из-за планирования ОС вы можете спать дольше, чем предполагалось. Страницы руководства для nanosleep говорят, что он будет использовать аргумент rem, чтобы сообщить вам оставшееся время, если вы получили сигнал, но он ничего не говорит о слишком долгое ожидание.

Вам нужно получить метку времени после каждого звонка до usleep, чтобы вы знали, как долго вы на самом деле спали. Если вы спали слишком коротко, вы добавляете дефицит. Если вы слишком долго спали, вы вычитаете лишний вес.

Вот пример того, как я сделал это в UFTP, многоадресного приложение для передачи файлов, для того, чтобы отправить пакеты с одинаковой скоростью:

int64_t diff_usec(struct timeval t2, struct timeval t1) 
{ 
    return (t2.tv_usec - t1.tv_usec) + 
      (int64_t)1000000 * (t2.tv_sec - t1.tv_sec); 
} 

... 

     int32_t packet_wait = 10000; 
     int64_t overage = 0, tdiff; 
     struct timeval current_sent, last_sent; 

     gettimeofday(&last_sent, NULL); 

     while(...) { 
      ... 

      if (packet_wait > overage) { 
       usleep(packet_wait - (int32_t)overage); 
      } 
      gettimeofday(&current_sent, NULL); 
      tdiff = diff_usec(current_sent, last_sent); 
      overage += tdiff - packet_wait; 

      last_sent = current_sent; 
      ... 
     } 
+0

Спасибо за ваш комментарий, это полезная информация и правильный способ контролировать время, если сон был слишком длинным. Однако это не то, о чем я прошу. Я пытаюсь понять, почему производительность цикла занятости изменяется по мере продвижения программы. – Rakurai

+0

@Rakurai Причина этого полностью зависит от ОС. Из-за этого оживленные циклы ожидания крайне непредсказуемы. Если вы не захотите углубиться в исходный код планировщика ОС (если вы работаете в Linux или какой-либо другой ОС с открытым исходным кодом), это не путь, по которому вы хотите спуститься. – dbush

1

Я бы сделал 2 балла - Из-за контекста swtiching sleep/usleep может спать больше времени, чем ожидалось. - Кроме того, если есть задача с более высоким приоритетом, например прерывания, может возникнуть ситуация, когда сон вообще не может быть выполнен.

Таким образом, если вы хотите точную задержку в вашем приложении вы можете использовать gettimeofday рассчитать промежуток времени, который может быть вычтен из-за задержки во время сна/USleep называют

Смежные вопросы