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

спасибо за совет относительно кэширующихся расширений PHP - Вы могли объяснить причины использования того по другому? Я услышал большие вещи о memcached через IRC, но никогда не слышал о APC - каковы Ваши мнения о них? Я предполагаю, что использование нескольких кэширующихся систем является довольно противоэффективным.

На самом деле, многие действительно используют APC и memcached вместе ...

5
задан Tim Post 6 November 2009 в 14:59
поделиться

10 ответов

Вам нужны таймеры с высоким разрешением.

В Linux, gettimeofday () - хороший выбор, он дает разрешение в микросекундах. В Windows обычно используется QueryPerformanceCounter () . Убедитесь, что вы запускаете свою функцию много раз, чтобы получить стабильные показания.

Быстрый пример для Linux:

struct timeval t0, t1;
unsigned int i;

gettimeofday(&t0, NULL);
for(i = 0; i < 100000; i++)
  function_to_measure();
gettimeofday(&t1, NULL);
printf("Did %u calls in %.2g seconds\n", i, t1.tv_sec - t0.tv_sec + 1E-6 * (t1.tv_usec - t0.tv_usec));

Вы, конечно, должны настроить счетчик (100 000) в соответствии с производительностью функции. Лучше всего, если функция действительно требует времени для запуска, иначе цикл и / или служебные данные могут доминировать.

11
ответ дан 18 December 2019 в 05:36
поделиться

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

Callgrind является частью Valgrind.

  • Art
4
ответ дан 18 December 2019 в 05:36
поделиться

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

gprof может помочь :)

Вот результат gprof, когда я запускаю свою программу в течение 10 секунд (имена функций изменены)

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 60.29      8.68     8.68 115471546     0.00     0.00  workalot
 39.22     14.32     5.64       46   122.70   311.32  work_b
  0.49     14.39     0.07                             inlined
  0.07     14.40     0.01       46     0.22     0.22  work_c
  0.00     14.40     0.00      460     0.00     0.00  find_minimum
  0.00     14.40     0.00      460     0.00     0.00  feedback
  0.00     14.40     0.00       46     0.00     0.00  work_a
3
ответ дан 18 December 2019 в 05:36
поделиться

Сохраните системное время перед входом в функцию. Сохраните системное время после выхода из функции. Вычтите разницу и сравните две реализации.

3
ответ дан 18 December 2019 в 05:36
поделиться

Checkout HighResTimer для высокой производительности таймер.

Вы, вероятно, обнаружите, что сохранение времени до / после недостаточно точно и, вероятно, приведет к 0, если у вас нет более продолжительной функции.

1
ответ дан 18 December 2019 в 05:36
поделиться

Все эти другие ответы использование некоторого варианта gettimeofday () для определения времени. Это довольно грубо, так как обычно вам нужно запускать ядро ​​много раз, чтобы получить воспроизводимые результаты. Помещение его в жесткий цикл изменяет состояние и кода, и кешей данных, поэтому эти результаты могут не указывать на реальную производительность.

Гораздо лучшей альтернативой является фактическое использование счетчика циклов ЦП. На x86 это можно сделать с помощью инструкции rdtsc . Это из x264 :

static inline uint32_t read_time(void)
{
    uint32_t a = 0;
#if defined(__GNUC__) && (defined(ARCH_X86) || defined(ARCH_X86_64))
    asm volatile( "rdtsc" :"=a"(a) ::"edx" );
#elif defined(ARCH_PPC)
    asm volatile( "mftb %0" : "=r" (a) );
#elif defined(ARCH_ARM)     // ARMv7 only
    asm volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(a) );
#endif
    return a;
}

Подробнее о профилировании с использованием различных аппаратных счетчиков см. PAPI . Для некоторых целей

3
ответ дан 18 December 2019 в 05:36
поделиться
  • Сохранить отметку времени до входа в функцию

  • Сохранить отметку времени после выхода из функции

  • Сравнить отметки времени

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

0
ответ дан 18 December 2019 в 05:36
поделиться

Ознакомьтесь с RDTSC , но лучше сделайте это как ниже.

0 - Вызов системной функции Sleep или Yield, чтобы при ее возврате у вас был новый временной интервал

1 - RDTSC

2 - Вызов вашей функции

3 - RDTSC

Если ваша функция является длительным, вам нужно использовать какой-то инструмент профилирования, например gprof (его очень легко использовать) и приложение Intel VTune (которое я давно не использовал). Увидев ответ Арта, я передумал с gprof на Callgrind. Раньше я использовал только инструмент Memcheck от Valgrind, и это был великолепный инструмент. Я раньше не пользовался Callgrind, но уверен, что он лучше, чем gprof ...

у вас есть новый временной интервал

1 - RDTSC

2 - Вызов вашей функции

3 - RDTSC

Если ваша функция долго выполняется, вы должны использовать какой-то инструмент профилирования, например gprof (это очень проста в использовании) и приложение Intel VTune (которым я давно не пользовался). Увидев ответ Арта, я передумал с gprof на Callgrind. Раньше я использовал только инструмент Memcheck от Valgrind, и это был великолепный инструмент. Я раньше не пользовался Callgrind, но уверен, что он лучше, чем gprof ...

у вас есть новый временной интервал

1 - RDTSC

2 - Вызов вашей функции

3 - RDTSC

Если ваша функция долго выполняется, вы должны использовать какой-то инструмент профилирования, например gprof (это очень проста в использовании) и приложение Intel VTune (которым я давно не пользовался). Увидев ответ Арта, я передумал с gprof на Callgrind. Раньше я использовал только инструмент Memcheck от Valgrind, и это был великолепный инструмент. Я раньше не пользовался Callgrind, но уверен, что он лучше, чем gprof ...

Раньше я использовал только инструмент Memcheck от Valgrind, и это был великолепный инструмент. Я раньше не пользовался Callgrind, но уверен, что он лучше, чем gprof ...

Раньше я использовал только инструмент Memcheck от Valgrind, и это был великолепный инструмент. Я раньше не пользовался Callgrind, но уверен, что он лучше, чем gprof ...

1
ответ дан 18 December 2019 в 05:36
поделиться

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


#include <time.h>

time_t starttime, endtime;

starttime = time(NULL);
for (i = 0; i < 1000000; i++)
{
    testfunc();
}
endtime = time(NULL);

printf("Time in seconds is %d\n", (int)(endtime-starttime));

Настройте количество итераций в соответствии с вашими потребностями. Если для одного вызова функции требуется 5 секунд, тогда вам понадобится большая чашка кофе на 1000000 итераций ... Когда разница составляет менее 1 секунды, даже для большого числа, вы должны 1) спросить себя, имеет ли это значение, и если да, 2 ) проверьте, есть ли в вашем любимом компиляторе встроенные функции профилирования.

0
ответ дан 18 December 2019 в 05:36
поделиться

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

#include <mach/mach_time.h>
#include <stdint.h>

int loopCount;

uint64_t startTime = mach_absolute_time( );
for (loopCount = 0; loopCount < iterations; ++loopCount) {
    functionBeingTimed( );
}
uint64_t endTime = mach_absolute_time( );
double averageTime = (double)(endTime-startTime) / iterations;

Это дает вам среднее время выполнения итераций вызовов функции. На это могут несколько повлиять эффекты вне вашего процесса в системе. Таким образом, вместо этого вы можете захотеть использовать самое быстрое время:

#include <mach/mach_time.h>
#include <stdint.h>

int loopCount;

double bestTime = __builtin_inf();
for (loopCount = 0; loopCount < iterations; ++loopCount) {
    uint64_t startTime = mach_absolute_time( );
    functionBeingTimed( );
    uint64_t endTime = mach_absolute_time( );
    double bestTime = __builtin_fmin(bestTime, (double)(endTime-startTime));
}

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

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

#include <mach/mach_time.h>
#include <sys/sysctl.h>
#include <stdint.h>

double ticksToNanoseconds(double ticks) {
    static double nanosecondsPerTick = 0.0;
    // The first time the function is called
    // ask the system how to convert mach
    // time units to nanoseconds
    if (0.0 == nanosecondsPerTick) {
        mach_timebase_info_data_t timebase;
        // to be completely pedantic, check the return code of this call:
        mach_timebase_info(&timebase);
        nanosecondsPerTick = (double)timebase.numer / timebase.denom;
    }
    return ticks * nanosecondsPerTick;
}

double nanosecondsToCycles(double nanoseconds) {
    static double cyclesPerNanosecond = 0.0;
    // The first time the function is called
    // ask the system what the CPU frequency is
    if (0.0 == cyclesPerNanosecond) {
        uint64_t freq;
        size_t freqSize = sizeof(freq);
        // Again, check the return code for correctness =)
        sysctlbyname("hw.cpufrequency", &freq, &freqSize, NULL, 0L );
        cyclesPerNanosecond = (double)freq * 1e-9;
    }
    return nanoseconds * cyclesPerNanosecond;
}

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

3
ответ дан 18 December 2019 в 05:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: