Измерение времени выполнения функции внутри ядра Linux - PullRequest
25 голосов
/ 11 января 2011

Я использую хуки модуля безопасности Linux для добавления некоторых пользовательских функций в системный вызов recv ().Я хочу измерить накладные расходы этой функции по сравнению с нетронутой recv ().Я написал простой tcp сервер, на котором я работаю с моим модулем и без него.Этот tcp сервер вызывает функцию recv () 'N' количество раз.Он измеряет время, затрачиваемое для каждого recv, примерно так:

clock_gettime(before);
recv()
clock_gettime(after);
global_time += after - before.

В конце я печатаю среднее время для одного recv () с помощью global_time / N.Давайте назовем это время «user_space_avg_recv».

Внутри моего модуля я хочу разместить функции измерения времени, чтобы вычислить точное время выполнения моего хука.Я попробовал 3 метода.

  1. Я использовал jiffies следующим образом:

    sj = jiffies;
    my_hook();
    ej = jiffies;
    current->total_oh = ej - sj;
    

    Но я вижу, что между значениями sj и ej нет никакой разницы.Следовательно, total_oh не изменяется.

  2. Я использовал current_kernel_time (), так как думал, что он возвращает время в наносекундах.Однако, опять же, не было никакой разницы до и после времени.

  3. Я использовал get_cycles.Я печатаю полные циклы, когда процесс завершается.Тем не менее, когда я конвертирую эти значения общего количества циклов в миллисекунды, получается, что они намного больше значения user_space_avg_recv.Это не имеет смысла, так как измеренное значение внутри ядра всегда меньше, чем значение времени, измеренное из пространства пользователя.Это может означать, что я либо измеряю не с использованием правильного API, либо делаю ошибку при преобразовании значения из циклов в миллисекунды.

В основном я использую следующую формулу для преобразования циклов в миллисекунды:

avg overhead of my hook in milliseconds = 
             (((cycles / 2.99) / 10^6) / N)

2.99, потому что моя тактовая частота составляет 2,99 ГГц

Некоторые моменты:

  • Моя программа пространства пользователя привязана к одному ядру с помощью набораaffinity.

  • Я использую ядро ​​2.6.22.14

  • Чтобы ядро ​​не переключало контексты, находясь в моем хуке, я использую preempt_disable () иpreempt_enable ().Таким образом, он не будет считать время выполнения других потоков ядра.Даже тогда, поскольку мой хук использует некоторый ввод-вывод, мой поток может добровольно освободить элемент управления или может произойти некоторое прерывание, которое может увеличить общее количество циклов.

Вопрос: Как я могу измеритьточно время выполнения функции внутри ядра?

Ответы [ 3 ]

21 голосов
/ 11 января 2011

Вы можете использовать API функции трассировки , чтобы получить трассировку всех вызовов и возвратов функций с высокоточными временными метками.Это включает в себя события прерывания и переключение контекста.Затем вы можете проанализировать полученную трассировку в пользовательском пространстве, чтобы получить точное представление о том, сколько времени занимает выполнение вашей функции.

Если вы не можете использовать API-интерфейс tracer, вы можете вызвать do_gettimeofday() вызов для получения метки времени с микросекундным разрешением или getnstimeofday() для разрешения наносекунд.Это те же самые функции, которые пользовательский вызов gettimeofday() использует для внутреннего использования.Конечно, для очень быстрых функций это может быть недостаточной точностью;Любая быстрая точность, чем эта, и вам, вероятно, нужно будет покопаться в коде таймера, чтобы увидеть, как он реализует преобразования циклов.Также обратите внимание, что только потому, что они имеют высокое разрешение, это не означает, что они обладают такой большой точностью, но они должны быть полезны для целей сравнительного анализа.

Обратите внимание, что любая форма трассировки приведет к дополнительной задержке - do_gettimeofday() требуетчисло атомарных операций сравнения и замены, и ftrace помещает код регистрации в каждой функции до и после .Это следует учитывать при интерпретации результатов.

3 голосов
/ 11 января 2011

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

double Microsecs()
{
   static struct timeval _t;  
   static struct timezone tz;  
   gettimeofday(&_t, &tz);  
   return   (double)_t.tv_sec + (double)_t.tv_usec/(1000*1000);
}

Чем вы звоните до и после разговора, который хотите, и видите, сколько времени.
Мы использовали этот метод для оценки операций чтения / записи / поиска с контролем времени ввода-вывода с целью оптимизации производительности, и у нас хорошие результаты.

НТН.

0 голосов
/ 12 января 2011

Вы пробовали использовать OProfile?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...