2009-11-06 2 views

ответ

2

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

+0

Спасибо, Джефф, это похоже на хороший подход. – Fred

+2

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

0
  • магазин временной метки перед тем ввода функции

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

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

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

3

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

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 
+1

Я согласен с этим * в целом *. Однако первая итерация может быть намного медленнее, чем остальная, из-за проблем с кешированием. Если процедура обычно выполняется только один раз, а не в замкнутой петле, это даст вам перекошенное изображение. OTOH, если процедура выполняется только один раз, вы не должны тратить драгоценное время, пытаясь ее профилировать или оптимизировать. –

+0

Спасибо PMg, я проверю gprof. Я заметил, что я даже установил его по умолчанию. – Fred

+0

T.E.D. делает несколько отличных баллов. Кэш-память процессора и кэширование ОС значительно улучшат производительность вашей функции на всех, кроме первой итерации, что даст вам среднюю производительность, намного превосходящую то, что вы получаете, если функция выполняется отдельно или между другими функциями, достаточно мясистыми, чтобы заменить содержимое кэша ЦП. Но это, вероятно, лучший простой метод профилирования, и он все равно даст вам хороший/приемлемый/ужасный показатель производительности. – Dogmang

9

Вы должны таймеры высокого разрешения.

В 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); 

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

+0

Спасибо за подсказку и пример. Я запускаю mac os здесь, так что gettimeofday() также доступен здесь. – Fred

+0

Это работает нормально, если функция зависит только от памяти и процессора и не меняет состояния (т. Е. Работает одинаково каждый раз). Если ваша функция имеет доступ к файлам, вас может обмануть кэширование файловой системы. – Adriaan

1

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

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

0

Проверьте RDTSC, но лучше сделайте это, как показано ниже.

0 - Сон или Выход функции системного вызова, так что, когда он вернется, у вас есть новый TimeSlice

1 - RDTSC

2 - Call ваша функция

3 - RDTSC

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

+0

Это интересно, я не знал, что для этого были аскетильные инструкции. Мог бы попробовать это, чтобы посмотреть, как это работает. – Fred

4

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

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

  • Art
0

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


#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) проверьте, имеет ли ваш любимый компилятор встроенные функции профилирования.

3

Фред, я заметил, что вы сказали в комментарии, что вы находитесь на 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; 

Это дает среднюю синхронизацию через 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)); 
} 

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

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

#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; 
} 

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

+0

Спасибо, Стивен, отлично! Я попробую это. – Fred

+0

Если вы столкнулись с какими-либо проблемами, дайте мне знать; Я набрал все это из памяти, поэтому я мог бы сделать ошибку где-то =) –

1

Все эти другие ответы используют некоторый вариант 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. Для некоторых целей, симуляторы (например Callgrind и прерывают на основе профайлеров (Oprofile) полезны

2

Здравствуйте я дам вам пример и объяснить:.

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

int main(void) 
{ 

    clock_t start_clk = clock(); 

    /* 
     put any code here 
    */ 

    printf("Processor time used by program: %lg sec.\n", \ 
    (clock() - start_clk)/(long double) CLOCKS_PER_SEC); 

    return 0; 
} 

выход: время процессора используется программа: 4,94066 е-324 сек

time.h:..

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

CLOCKS_PER_SEC - это макрос, объявленный во времени.h, используйте его в качестве знаменателя для преобразования значения в секунды.

необходимо, чтобы привести его к длинной двойной по двум причинам:

  1. мы не знаем, какой тип clock_t на самом деле, но мы хотим напечатать его (что преобразование будет вы кладете в Printf?) ,
  2. long double - очень точный тип, который может представлять собой действительно небольшие значения.