Интересная штучка этот gprof.
Сейчас с набегу прочитал бОльшую часть руководства по GNU Profiler. Для тех, кто совсем не в курсе, вот цитата о том, что такое профилирование:
Чтобы использовать gprof нужно скомпилировать программу с поддержкой профилирования — для этого используется опция -pg. Далее запускайте вашу программу, работайте в ней как обычно и потом после выхода в том же каталоге появится файл gmon.out Это файл с профилем вашей программы. Чтобы извлечь из него информацию нужно запустить gprof, передав ему в качестве параметра имя проверяемой программы. На стандартный вывод вы получите множество информации. Помимо самой инфы там есть много объяснений что и как на английском. Как правило, она мало полезна и поэтому её можно отключать с помощью опции -b.
Вообщем всего не расскажешь, да я и не знаю всех тонкостей. Лучше приведу примерчик. Я экспериментировал на уже упомянавшейся тут мной программе — wmdiskmon.
Вот пятёрка самых часто вызываемых функций. Причем, первые две вообще самые часто вызываемые :) И неудивительно, т.к. dockapp_nextevent_or_timeout() вызывается в программе в бесконечном цикле, анализирует события и в зависимости от них что-то делает. В частности именно она и вызывает dockapp_copyarea(). О том кто кого вызывает можно узнать из Call Graph. (чёто я реально сумбурно замутил тут %)) )
После моего копания. Решил я глянуть в эти ф-ции. Гы) Вот код dockapp_copyarea():
Как видно эта ф-ция почти ничего не делает, а является обёрткой над XCopyArea(). Разница лишь в количестве аргументов. Смысл делать обёртку, как я понял, в том чтобы ограничить область видимости для некоторых переменных, в частности, для display. Вообщем, это конечно не С++ с его приватными, публичными и защищенными членами класса и областями видимости... :) Теперь, что я думаю. Если те две переменных, ради которых и затевался весь сыр-бор, сделать глобальными, тогда отпадает необходимость в этой ф-ции. И выигрыш явно будет. Насколько я в этом всём смыслю, то при входе в функцию в стек отправляется текущий адрес (это чтобы знать уда вернуться потом) и все аргументы для неё. В итоге получается, что каждый раз вызывая эту ф-цию мы заталкиваем в стек аж по восемь значений (даже девять). Неоптимально...
(( Не знаю стОит ли с этим возиться... но даже мои умозаключения уже полезны и интересны :) (мне разумеется) ))
Вот код для dockapp_nextevent_or_timeout():
Я не всё здесь понимаю, но если бы я хотел оптимизировать, то мне кажется, что нужно сделать так чтобы каждый раз не создавалась структура timeout в локальной области видимости. Может лучше стОит завести её в каком-нибудь другом месте, а здесь использовать лишь ссылку. Или... гм.. сейчас вот вспомнил, что можно как-то сделать чтобы переменная, созданная в ф-ции не уничтожалась после того, как ф-ция отработает (не static ли ?)... Вообщем, есть такое. Тогда было бы удобно... и оптимизация бы, по моему мнению была...
Вот. Простите, что этот пост больше похож на поток моих мыслей, чем на структурированное изложение информации. Просто я сейчас как раз размышляю :)
Ладно. Резюме: gprof хороший инструмент, который поможет программисту понять, какие ф-ции лучше всего оптимизировать и сфокусирует его на правильной цели :)
P.S. 2
voins: скажи, я правильно вижу пути оптимизации?
P.P.S. Обана! :))) Короче, этот miliseconds тоже бы как-нить не передавать... Его глобально бы как-нить... Объясняю почему: программе, можно указать интервал между обновлениями, который по умолчанию равен 5 секундам. Так вот.. эта переменная в main.c берётся из опций командной строки. Она определяется при старте программы и больше не меняется. Но, так как она локальная, то и приходится каждый раз её передавать. Гм... Вообщем, эту структуру нужно объявить локальной и вычислять всего один раз. Вот.
Сейчас с набегу прочитал бОльшую часть руководства по GNU Profiler. Для тех, кто совсем не в курсе, вот цитата о том, что такое профилирование:
Профилирование позволяет вам изучить, где ваша программа расходует свое время и какие функции вызывали другие функции, пока программа исполнялась. Эта информация может указать вам на ту часть программы, которая исполняется медленнее, чем вы ожидали, и которая может быть кандидатом на переписывание, чтобы ускорить выполнение программы. Эта информация также подскажет вам, какие функции вызывались чаще или реже, чем вы ожидали. Это может помочь вам отметить ошибки, которые иначе остались бы незамеченными.
Чтобы использовать gprof нужно скомпилировать программу с поддержкой профилирования — для этого используется опция -pg. Далее запускайте вашу программу, работайте в ней как обычно и потом после выхода в том же каталоге появится файл gmon.out Это файл с профилем вашей программы. Чтобы извлечь из него информацию нужно запустить gprof, передав ему в качестве параметра имя проверяемой программы. На стандартный вывод вы получите множество информации. Помимо самой инфы там есть много объяснений что и как на английском. Как правило, она мало полезна и поэтому её можно отключать с помощью опции -b.
Вообщем всего не расскажешь, да я и не знаю всех тонкостей. Лучше приведу примерчик. Я экспериментировал на уже упомянавшейся тут мной программе — wmdiskmon.
[c0der@mycomp ~/tmp/gprof/wmdiskmon-0.0.1/src]$ gprof -baQ ./wmdiskmon | sed '1,5d' H -7
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
0.00 0.00 0.00 142 0.00 0.00 dockapp_nextevent_or_timeout
0.00 0.00 0.00 139 0.00 0.00 dockapp_copyarea
0.00 0.00 0.00 6 0.00 0.00 dockapp_xpm2pixmap
0.00 0.00 0.00 6 0.00 0.00 xstrdup
0.00 0.00 0.00 3 0.00 0.00 dockapp_getcolor
Вот пятёрка самых часто вызываемых функций. Причем, первые две вообще самые часто вызываемые :) И неудивительно, т.к. dockapp_nextevent_or_timeout() вызывается в программе в бесконечном цикле, анализирует события и в зависимости от них что-то делает. В частности именно она и вызывает dockapp_copyarea(). О том кто кого вызывает можно узнать из Call Graph. (чёто я реально сумбурно замутил тут %)) )
После моего копания. Решил я глянуть в эти ф-ции. Гы) Вот код dockapp_copyarea():
void
dockapp_copyarea(Pixmap src, Pixmap dist, int x_src, int y_src, int w, int h,
int x_dist, int y_dist)
{
XCopyArea(display, src, dist, gc, x_src, y_src, w, h, x_dist, y_dist);
}
Как видно эта ф-ция почти ничего не делает, а является обёрткой над XCopyArea(). Разница лишь в количестве аргументов. Смысл делать обёртку, как я понял, в том чтобы ограничить область видимости для некоторых переменных, в частности, для display. Вообщем, это конечно не С++ с его приватными, публичными и защищенными членами класса и областями видимости... :) Теперь, что я думаю. Если те две переменных, ради которых и затевался весь сыр-бор, сделать глобальными, тогда отпадает необходимость в этой ф-ции. И выигрыш явно будет. Насколько я в этом всём смыслю, то при входе в функцию в стек отправляется текущий адрес (это чтобы знать уда вернуться потом) и все аргументы для неё. В итоге получается, что каждый раз вызывая эту ф-цию мы заталкиваем в стек аж по восемь значений (даже девять). Неоптимально...
(( Не знаю стОит ли с этим возиться... но даже мои умозаключения уже полезны и интересны :) (мне разумеется) ))
Вот код для dockapp_nextevent_or_timeout():
Bool
dockapp_nextevent_or_timeout(XEvent *event, unsigned long miliseconds)
{
struct timeval timeout;
fd_set rset;
XSync(display, False);
if (XPending(display)) {
XNextEvent(display, event);
return True;
}
timeout.tv_sec = miliseconds / 1000;
timeout.tv_usec = (miliseconds % 1000) * 1000;
FD_ZERO(&rset);
FD_SET(ConnectionNumber(display), &rset);
if (select(ConnectionNumber(display)+1, &rset, NULL, NULL, &timeout) > 0) {
XNextEvent(display, event);
if (event->type == ClientMessage) {
if ((Atom)event->xclient.data.l[0] == delete_win) {
XDestroyWindow(display,event->xclient.wi ndow);
XCloseDisplay(display);
exit(0);
}
}
if (dockapp_iswindowed) {
event->xbutton.x -= offset_w;
event->xbutton.y -= offset_h;
}
return True;
}
return False;
}
Я не всё здесь понимаю, но если бы я хотел оптимизировать, то мне кажется, что нужно сделать так чтобы каждый раз не создавалась структура timeout в локальной области видимости. Может лучше стОит завести её в каком-нибудь другом месте, а здесь использовать лишь ссылку. Или... гм.. сейчас вот вспомнил, что можно как-то сделать чтобы переменная, созданная в ф-ции не уничтожалась после того, как ф-ция отработает (не static ли ?)... Вообщем, есть такое. Тогда было бы удобно... и оптимизация бы, по моему мнению была...
Вот. Простите, что этот пост больше похож на поток моих мыслей, чем на структурированное изложение информации. Просто я сейчас как раз размышляю :)
Ладно. Резюме: gprof хороший инструмент, который поможет программисту понять, какие ф-ции лучше всего оптимизировать и сфокусирует его на правильной цели :)
P.S. 2
P.P.S. Обана! :))) Короче, этот miliseconds тоже бы как-нить не передавать... Его глобально бы как-нить... Объясняю почему: программе, можно указать интервал между обновлениями, который по умолчанию равен 5 секундам. Так вот.. эта переменная в main.c берётся из опций командной строки. Она определяется при старте программы и больше не меняется. Но, так как она локальная, то и приходится каждый раз её передавать. Гм... Вообщем, эту структуру нужно объявить локальной и вычислять всего один раз. Вот.