На SO есть довольно много вопросов о профилировании производительности, но я не вижу полной картины. Есть довольно много проблем, и большинство вопросов и ответов игнорируют все, кроме нескольких, или не оправдывают свои предложения.
Что мне интересно. Если у меня есть две функции, которые делают одно и то же, и мне интересно узнать разницу в скорости, имеет ли смысл тестировать это без внешних инструментов, с таймерами, или это скомпилировано в тестировании, сильно повлияет на результаты?
Я спрашиваю об этом, потому что, если это разумно, как программисту на C ++, я хочу знать, как это лучше всего сделать, поскольку они намного проще, чем использование внешних инструментов. Если это имеет смысл, давайте продолжим со всеми возможными ловушками:
Рассмотрим этот пример. Следующий код показывает 2 способа сделать то же самое:
#include <algorithm>
#include <ctime>
#include <iostream>
typedef unsigned char byte;
inline
void
swapBytes( void* in, size_t n )
{
for( size_t lo=0, hi=n-1; hi>lo; ++lo, --hi )
in[lo] ^= in[hi]
, in[hi] ^= in[lo]
, in[lo] ^= in[hi] ;
}
int
main()
{
byte arr[9] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
const int iterations = 100000000;
clock_t begin = clock();
for( int i=iterations; i!=0; --i )
swapBytes( arr, 8 );
clock_t middle = clock();
for( int i=iterations; i!=0; --i )
std::reverse( arr, arr+8 );
clock_t end = clock();
double secSwap = (double) ( middle-begin ) / CLOCKS_PER_SEC;
double secReve = (double) ( end-middle ) / CLOCKS_PER_SEC;
std::cout << "swapBytes, for: " << iterations << " times takes: " << middle-begin
<< " clock ticks, which is: " << secSwap << "sec." << std::endl;
std::cout << "std::reverse, for: " << iterations << " times takes: " << end-middle
<< " clock ticks, which is: " << secReve << "sec." << std::endl;
std::cin.get();
return 0;
}
// Output:
// Release:
// swapBytes, for: 100000000 times takes: 3000 clock ticks, which is: 3sec.
// std::reverse, for: 100000000 times takes: 1437 clock ticks, which is: 1.437sec.
// Debug:
// swapBytes, for: 10000000 times takes: 1781 clock ticks, which is: 1.781sec.
// std::reverse, for: 10000000 times takes: 12781 clock ticks, which is: 12.781sec.
Выпуски:
- Какие таймеры использовать и как фактически использовать процессорное время для рассматриваемого кода?
- Каковы эффекты оптимизации компилятора (поскольку эти функции просто меняют байты назад и вперед, очевидно, что наиболее эффективная вещь - вообще ничего не делать)?
- Учитывая результаты, представленные здесь, считаете ли вы, что они точны (могу вас заверить, что многократные прогоны дают очень похожие результаты)? Если да, можете ли вы объяснить, как std :: reverse становится таким быстрым, учитывая простоту пользовательской функции. У меня нет исходного кода из версии vc ++, которую я использовал для этого теста, но здесь - реализация из GNU. Это сводится к функции iter_swap , которая для меня совершенно непонятна. Можно ли ожидать, что это будет выполняться вдвое быстрее, чем эта пользовательская функция, и если да, то почему?
Размышления:
Похоже, предлагаются два высокоточных таймера: clock () и QueryPerformanceCounter (для окон). Очевидно, что мы хотели бы измерять время процессора нашего кода, а не реальное время, но, насколько я понимаю, эти функции не предоставляют такой функциональности, поэтому другие процессы в системе будут мешать измерениям. Эта страница в библиотеке gnu c, кажется, противоречит этому, но когда я ставлю точку останова в vc ++, отлаживаемый процесс получает много тактов, даже если он был приостановлен (я не тестировал под gnu). Я пропускаю альтернативные счетчики для этого или нам нужны хотя бы специальные библиотеки или классы для этого? Если нет, достаточно ли хороших часов в этом примере или будет ли причина использовать QueryPerformanceCounter?
Что мы можем знать наверняка без средств отладки, разборки и профилирования? Что-нибудь на самом деле происходит? Является ли вызов функции встроенным или нет? При регистрации в отладчике байты действительно меняются местами, но я предпочел бы знать из теории почему, чем из тестирования.
Спасибо за любые указания.
обновление
Благодаря подсказке от tojas функция swapBytes теперь работает так же быстро, как и std :: reverse. Я не смог понять, что временная копия в случае байта должна быть только регистром, и поэтому очень быстрая. Элегантность может ослепить вас.
inline
void
swapBytes( byte* in, size_t n )
{
byte t;
for( int i=0; i<7-i; ++i )
{
t = in[i];
in[i] = in[7-i];
in[7-i] = t;
}
}
Благодаря совету от ChrisW Я обнаружил, что в Windows вы можете получить фактическое время ЦП, затраченное на процесс (читай: ваш) Инструментарий управления Windows . Это определенно выглядит интереснее, чем высокоточный счетчик.