Подтвердить что ты не робот

Измерение времени выполнения функции внутри ядра linux

Я использую крючки модуля безопасности 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 метода.

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

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

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

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

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

НТН.

Ответ 3

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