2013-11-24 3 views
12

Я пытаюсь охарактеризовать дрожание таймера на Linux. Моя задача - запустить таймеры 100 мс и посмотреть, как работают номера.Какова точность интервальных таймеров в Linux?

Я работаю над многоядерной машиной. Я использовал стандартную пользовательскую программу с setitimer(), ту же работу, что и root, а затем с близостью процессора и, наконец, с близостью процессора и приоритетом процесса. Затем я запускал то же самое с ядром PREEMPT_RT, а затем запускал примеры с помощью clock_nanosleep(), как в демо-коде на странице PREEMPT_RT. Из всех прогонов производительность таймера была очень схожей, без реальной разницы, несмотря на изменения.

Наша цель - постоянный таймер. Лучшим худшим случаем, который я мог получить регулярно, было около 200us. Гистограмма для всех случаев показывает действительно странное поведение. Во-первых, я не ожидал, что таймеры будут срабатывать раньше. Но они это делают. И, как вы можете видеть на гистограмме, я получаю впадины с обеих сторон смещения 0. Они видны в трех полосах на втором графике. На первом графике ось X находится в микросекундах. На втором графике ось Y находится в микросекундах.

Я провел 30-секундный тест (то есть 300 событий таймера) 100 раз, чтобы сгенерировать некоторые цифры. Вы можете увидеть их на следующих диаграммах. В 200us большое падение. Все временные смещения таймера событий 30000 отображаются на втором графике, где вы можете увидеть некоторые выбросы.

X axis is in microsecondsY axis is in microseconds

Так что вопрос, кто-нибудь другой сделал такой анализ раньше? Вы видели такое же поведение? Мое предположение заключается в том, что ядро ​​RT помогает системам с большими нагрузками, но в нашем случае это не помогло устранить дрожание таймера. Это ваш опыт?

Вот код. Как я уже говорил, я изменил код примера на сайте PREEMPT_RT, который использует функцию clock_nanosleep(), поэтому я не буду включать в себя мои минимальные изменения для этого.

#include <signal.h> 
#include <stdio.h> 
#include <string.h> 
#include <sys/time.h> 
#include <stdlib.h> 

#define US_PER_SEC 1000000 
#define WAIT_TIME 100000 
#define MAX_COUNTER 300 

int counter = 0; 
long long last_time = 0; 
static long long times[MAX_COUNTER]; 
int i = 0; 

struct sigaction sa; 

void timer_handler(int signum) 
{ 
    if (counter > MAX_COUNTER) 
    { 
     sigaction(SIGALRM, &sa, NULL); 
     for (i = 0; i < MAX_COUNTER; i++) 
     { 
      printf("%ld\n", times[i]); 
     } 
     exit(EXIT_SUCCESS); 
    } 

    struct timeval t; 
    gettimeofday(&t, NULL); 

    long long elapsed = (t.tv_sec * US_PER_SEC + t.tv_usec); 

    if (last_time != 0) 
    { 
     times[counter] = elapsed - last_time; 
     ++counter; 
    } 

    last_time = elapsed; 
} 

int main() 
{ 
    struct itimerval timer; 

    memset(&sa, 0, sizeof(sa)); 

    sa.sa_handler = &timer_handler; 

    sigaction(SIGALRM, &sa, NULL); 

    timer.it_value.tv_sec = 0; 
    timer.it_value.tv_usec = 1; 

    timer.it_interval.tv_sec = 0; 
    timer.it_interval.tv_usec = WAIT_TIME; 

    setitimer(ITIMER_REAL, &timer, NULL); 

    while (1) 
    { 
     sleep(1); 
    } 
} 

EDIT: это на Xeon E31220L, работающий на 2,2 ГГц, работает x86_64 Fedora Core 19.

+0

Это на x86 вправо? Какое оборудование? Здесь задействовано так много переменных, я думаю, что невероятно сложно сделать какие-либо обоснованные обобщения. –

+0

Добавлена ​​информация о архитектуре процессора. Благодаря! –

+0

Пожалуйста, включите источник функции 'timer_handler()'. – caf

ответ

1

Ты прав, чтобы не ожидать Таймеры стрелять рано - и они этого не делают. Недостаток заключается в том, что вы не измеряете время с момента истекшего предыдущего таймера - вы измеряете время с предыдущего звонка gettimeofday(). Если между истечением таймера и задержкой истекает время задержки, и вы увидите, что этот gettimeofday() работает с опозданием, , а следующий из них запускается на той же сумме.

Вместо того, чтобы регистрировать разницу между последующими вызовами gettimeofday(), попробуйте зарегистрировать абсолютное время возврата, а затем сравните возвращаемые времена с N * 100 мс после первоначального времени.

Для того, чтобы помочь вам, вам необходимо установить политику планировщика в реальном времени для вашей тестовой программы (SCHED_FIFO или SCHED_RR), для которой требуется root.

+0

Для PREEMPT_RT я сделал именно это. Я подозревал, что мои вызовы gettimeofday() являются подозрительными, и ваше объяснение очищает все. Причина, по которой PREEMPT_RT проявляет такое же поведение, состоит в том, что я использую один и тот же код для сравнения времени. Спасибо за ответ. –

+0

Как в стороне, вместо 'sleep (1);' вы можете использовать 'pause();' в этом цикле. – caf

+1

Кроме того, я [слышал] (http://www.orocos.org/forum/orocos/orocos-users/how-does-orocos-do-real-time-os-access#comment-33765), что ' gettimeofday() 'не работает в режиме реального времени в' PREEMPT_RT'; правильной альтернативой является 'clock_gettime()' – crosstalk