Как вы измеряете время, необходимое для выполнения функции?

StackOverflow https://stackoverflow.com/questions/68907

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Этот конкретный вопрос относится к встроенной системе, запрограммированной на C или C ++.

Это было полезно?

Решение

Лучший способ сделать это во встроенной системе - установить внешний аппаратный PIN-код при входе в функцию и сбросить его при выходе из функции.Желательно, чтобы это делалось с помощью небольшой инструкции по сборке, чтобы вы не слишком искажали свои результаты.

Редактировать:Одним из преимуществ является то, что вы можете сделать это в своем реальном приложении, и вам не нужен какой-либо специальный тестовый код.Подобные внешние отладочные контакты являются (должны быть!) стандартной практикой для каждой встроенной системы.

Другие советы

Есть три возможных решения:

Аппаратное решение:

Используйте свободный выходной вывод процессора и подсоедините к нему осциллограф или логический анализатор.Инициализируйте pin-код в низкое состояние, непосредственно перед вызовом функции, которую вы хотите измерить, подтвердите pin-код в высокое состояние и сразу после возврата из функции снимите pin-код.


    *io_pin = 1;
    myfunc();
    *io_pin = 0;

Решение проблемы книжного червя:

Если функция довольно мала, и вы можете управлять дизассемблированным кодом, вы можете взломать книгу данных архитектуры процессора и подсчитать циклы, которые потребуются процессору для выполнения каждой инструкции.Это даст вам необходимое количество циклов.
Time = # циклов * Тактовая частота процессора / тактовые тики в соответствии с инструкциями

Это проще сделать для небольших функций или кода, написанного на ассемблере (например, для микроконтроллера PIC).

Решение для счетчика временных меток:

Некоторые процессоры имеют счетчик временных меток, который увеличивается с высокой скоростью (каждые несколько тактов процессора).Просто прочитайте временную метку до и после выполнения функции.Это даст вам время, затраченное на проверку, но помните, что вам, возможно, придется иметь дело с опрокидыванием счетчика.

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

итак:

// begin timing
for (int i = 0; i < 10000; i++) {
    invokeFunction();
}
// end time
// divide by 10000 to get actual time.

если вы используете Linux, вы можете установить время выполнения программы, введя в командной строке:

time [funtion_name]

если вы запускаете только функцию в main() (предполагая C ++), остальное время работы приложения должно быть незначительным.

Я повторяю вызов функции много раз (миллионы), но также использую следующий метод, чтобы уменьшить накладные расходы цикла:

start = getTicks();

repeat n times {
    myFunction();
    myFunction();
}

lap = getTicks();

repeat n times {
    myFunction();
}

finish = getTicks();

// overhead + function + function
elapsed1 = lap - start;

// overhead + function
elapsed2 = finish - lap;

// overhead + function + function - overhead - function = function
ntimes = elapsed1 - elapsed2;

once = ntimes / n; // Average time it took for one function call, sans loop overhead

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

start_time = timer
function()
exec_time = timer - start_time

Windows XP/ NT Embedded или Windows CE/Mobile

Вы можете использовать QueryPerformanceCounter(), чтобы получить значение ОЧЕНЬ БЫСТРОГО счетчика до и после вашей функции.Затем вы вычитаете эти 64-битные значения и получаете дельта-"тики".Используя QueryPerformanceCounterFrequency(), вы можете преобразовать "дельта-тики" в фактическую единицу времени.Вы можете обратиться к документации MSDN об этих вызовах WIN32.

Другие встроенные системы

Без операционных систем или только с базовыми операционными системами вам придется:

  • запрограммируйте один из внутренних процессорных таймеров на свободный запуск и подсчет.
  • настройте его так, чтобы он генерировал прерывание при переполнении таймера, и в этой процедуре прерывания увеличьте переменную "переноса" (это для того, чтобы вы могли фактически измерять время, превышающее разрешение выбранного таймера).
  • перед выполнением функции вы сохраняете КАК значение "переноса", так и значение регистра процессора, содержащего текущие такты для настроенного вами таймера подсчета.
  • то же самое после вашей функции
  • вычтите их, чтобы получить значение дельта-счетчика.
  • оттуда просто нужно знать, сколько времени означает тик на вашем процессоре / оборудовании, учитывая внешние часы и умножение, которое вы настроили при настройке вашего таймера.Вы умножаете эту "длину тика" на "дельта-тики", которые вы только что получили.

ОЧЕНЬ ВАЖНЫЙ Не забудьте отключить прерывания до и восстановить их после получения этих значений таймера (включая перенос и значение регистра), иначе вы рискуете сохранить неправильные значения.

Примечания

  • Это очень быстро, потому что для отключения прерываний, сохранения двух целых значений и повторного включения прерываний требуется всего несколько инструкций по сборке.Фактическое вычитание и преобразование в единицы реального времени происходит ЗА ПРЕДЕЛАМИ зоны измерения времени, то есть ПОСЛЕ ВЫПОЛНЕНИЯ вашей функции.
  • Возможно, вы захотите поместить этот код в функцию, чтобы повторно использовать этот код повсюду, но это может немного замедлить работу из-за вызова функции и переноса всех регистров в стек, плюс параметров, а затем повторного их использования.Во встроенной системе это может быть существенно.Возможно, было бы лучше вместо этого использовать МАКРОСЫ на C или написать свою собственную процедуру сборки, сохраняющую / восстанавливающую только соответствующие регистры.

Зависит от вашей встроенной платформы и от того, какой тип синхронизации вы ищете.Для встроенного Linux есть несколько способов, которыми вы можете это сделать.Если вы хотите измерить количество процессорного времени, используемого вашей функцией, вы можете выполнить следующее:

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

#define SEC_TO_NSEC(s) ((s) * 1000 * 1000 * 1000)

int work_function(int c) {
    // do some work here
    int i, j;
    int foo = 0;
    for (i = 0; i < 1000; i++) {
        for (j = 0; j < 1000; j++) {
            for ^= i + j;
        }
    }
}

int main(int argc, char *argv[]) {
    struct timespec pre;
    struct timespec post;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &pre);
    work_function(0);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &post);

    printf("time %d\n",
        (SEC_TO_NSEC(post.tv_sec) + post.tv_nsec) -
        (SEC_TO_NSEC(pre.tv_sec) + pre.tv_nsec));
    return 0;
}

Вам нужно будет связать это с библиотекой реального времени, просто используйте следующее для компиляции вашего кода:

gcc -o test test.c -lrt

Возможно, вы также захотите ознакомиться со справочной страницей на clock_gettime существуют некоторые проблемы с запуском этого кода в системе на базе SMP, которые могут привести к аннулированию вашего тестирования.Вы могли бы использовать что-то вроде sched_setaffinity() или в командной строке cpuset принудительно использовать код только на одном ядре.

Если вы хотите измерить пользовательское и системное время, то вы могли бы использовать times(NULL) который возвращает что-то вроде jiffies.Или вы можете изменить параметр для clock_gettime() От CLOCK_THREAD_CPUTIME_ID Для CLOCK_MONOTONIC... но будьте осторожны с обертыванием вокруг CLOCK_MONOTONIC.

Что касается других платформ, вы действуете сами по себе.

Нарисовал

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

Пример:

#define TICK_INTERVAL 1    // milliseconds between ticker interrupts
static unsigned long tickCounter;

interrupt ticker (void)  
{
    tickCounter += TICK_INTERVAL;
    ...
}

unsigned in GetTickCount(void)
{
    return tickCounter;
}

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

int function(void)
{
    unsigned long time = GetTickCount();

    do something ...

    printf("Time is %ld", GetTickCount() - ticks);
}

В терминале OS X (и, вероятно, в Unix тоже) используйте "time".:

time python function.py

Если код является .Net, используйте класс stopwatch (.net 2.0+), А НЕ DateTime.Now.Дата и время.Сейчас обновляется недостаточно точно и даст вам сумасшедшие результаты

Если вы ищете разрешение менее миллисекунды, попробуйте один из этих методов синхронизации.Все они обеспечат вам разрешение по крайней мере за десятки или сотни микросекунд:

Если это встроенный Linux, посмотрите на таймеры Linux:

http://linux.die.net/man/3/clock_gettime

Встроенная Java, посмотрите на nanoTime (), хотя я не уверен, что это есть во встроенной версии:

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/System.html#nanoTime ()

Если вы хотите разобраться со счетчиками оборудования, попробуйте PAPI:

http://icl.cs.utk.edu/papi/

В противном случае вы всегда можете перейти на ассемблер.Вы могли бы взглянуть на исходный код PAPI для вашей архитектуры, если вам нужна некоторая помощь в этом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top