Какова точность интервальных таймеров в Linux?

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

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

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

Я провел тест 30s (то есть 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("%ldn", 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.

1 ответов


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

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

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