спасибо за совет относительно кэширующихся расширений PHP - Вы могли объяснить причины использования того по другому? Я услышал большие вещи о memcached через IRC, но никогда не слышал о APC - каковы Ваши мнения о них? Я предполагаю, что использование нескольких кэширующихся систем является довольно противоэффективным.
На самом деле, многие действительно используют APC и memcached вместе ...
Вам нужны таймеры с высоким разрешением.
В 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) в соответствии с производительностью функции. Лучше всего, если функция действительно требует времени для запуска, иначе цикл и / или служебные данные могут доминировать.
Профилировщик Callgrind с открытым исходным кодом (для Linux) - действительно отличный способ измерить производительность. В сочетании с KCacheGrind вы получаете действительно отличную визуализацию того, где вы проводите время.
Callgrind является частью Valgrind.
Запустите его (их) несколько миллионов раз (каждый) и измерьте время, которое это займет.
Тот, который завершается быстрее, имеет лучшую производительность.
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
Сохраните системное время перед входом в функцию. Сохраните системное время после выхода из функции. Вычтите разницу и сравните две реализации.
Checkout HighResTimer для высокой производительности таймер.
Вы, вероятно, обнаружите, что сохранение времени до / после недостаточно точно и, вероятно, приведет к 0, если у вас нет более продолжительной функции.
Все эти другие ответы использование некоторого варианта 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 . Для некоторых целей
Сохранить отметку времени до входа в функцию
Сохранить отметку времени после выхода из функции
Сравнить отметки времени
Убедитесь, что , чтобы использовать значительную выборку, поскольку временное разрешение может отличаться от результатов. Это особенно верно для краткосрочных функций. Используйте таймеры с высоким разрешением (разрешение в микросекундах доступно на большинстве платформ).
Ознакомьтесь с 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 ...В качестве наиболее простого и переносимого подхода вы можете использовать стандартную функцию 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 ) проверьте, есть ли в вашем любимом компиляторе встроенные функции профилирования.
Фред, я заметил, что вы сказали в комментарии, что используете 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;
}
Имейте в виду, что преобразование в наносекунды всегда будет правильным, но преобразование в циклы может происходить по-разному, поскольку современные процессоры не работают с одной фиксированной скоростью. Тем не менее, в целом он работает довольно хорошо.