О prelink: предварительное связывание как способ повышения быстродействия

Алексей Федорчук
4 октября 2005 г

Материал отправлен на свалку истории, поскольку предмет его утратил актуальность.

Один из резервов верховного главнокомандования в деле повышения быстродействия Linux-системы — механизм предварительного связывания или, по простому, прелинкинга (prelinking).

Чтобы разобраться, что происходит при прелинкинге, нужно вспомнить о том, что подавляющее большинство Linux-приложений не содержит в себе весь необходимый для их работы код, а использует т.н. разделяемые библиотеки. И обычно программы при сборке связываются с такими библиотеками динамически, то есть необходимые функции вызываются из них в ходе загрузки программы. В одних случаях это происходит быстро, в других — раздражающе медленно. Печальным примером последнего является KDE — в частности, из-за громоздкости и сложности опорной библиотеки Qt, написанной на Си++. И бороться с этим перекомпиляцией и оптимизацией почти бесполезно — выигрыш в скорости не превышает нескольких процентов.

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

Для операции прелинкинга необходимы:

  • дистрибутив Linux с достаточно свежими версиями главной системной библиотеки glibc (версии 2.3.1 и выше) и сборочного комплекта binutils (версии 2.13 и выше); последний пакет рекомендуется в исполнении kernel.org, хотя я использовал и вариант с gnu.org — также без всяких проблем;
  • библиотека libelf версии 0.7.0 или, лучше, 0.8.2;
  • собственно программа prelink, входящая в состав большинства дистрибутивов.

Для начала устанавливаем libelf — обычным образом, распаковав архив и запустив последовательно ./configure, make, make install. Затем пытаемся проделать ту же процедуру с prelink — и в ходе исполнения make благополучным образом получаем сообщение об ошибке, причем, что самое обидное и парадоксальное — на стадии сборки компонентов для архитектуры PPC, нужной нам, как зайцу стоп-сигнал. Я довольно долго ломал голову, как побороть эту напасть, пока в недрах дерева портежей Gentoo не обнаружил специально предназначенный к тому патч. Он залегает в отростке portage/sys-devel/prelink/files и носит имя prelink-20030505-glibc231.patch (к сожалению, более простого способа его получения я не обнаружил — разве что самому написать). Несовпадение номеров версий патча и собственно prelink смущать тут не должно — все описанное проверено на собственной шкуре и работает.

Итак, распаковываем архив prelink, переходим в образовавшийся каталог и налагаем патч:

$ patch -Np1 -i /path/prelink-20030505-glibc231.patch

Теперь можно безбоязненно запускать ./configure, make, make install — все пройдет нормально. В результате мы получаем исполняемый файл /usr/local/sbin (если, конечно, при конфигурировании не был задан другой префикс — логичным представляется использование --prefix=/usr/sbin).

Современное примечание: сказанное выше имело силу более года назад. В настоящее время проблема эта, вроде бы рассосалась.

Прежде чем запускать программу связывания, необходимо выполнить некоторые конфигурационные мероприятия. А именно — скопировать из каталога doc в дереве исходников prelink файл prelink.conf:

$ cp /path_to_prelink-src/doc/prelink.conf /etc

А затем внести в него все пути к исполняемым файлам и библиотекам, которые вы собираетесь подвергнуть процедуре предварительного связывания. В результате он приобретет вид вроде следующего:

-l /bin
-l /usr/bin
-l /sbin
-l /usr/sbin
-l /usr/X11R6/bin
...
-l /lib
-l /usr/lib
-l /usr/X11R6/lib
...

Пользователям KDE (а именно к ним, как и к пользователям GNOME, в первую голову обращен этот раздел заметки) следует проследить, чтобы в этом описании файла /etc/prelink.conf присутствовали пути к библиотекам Qt, kdelibs и исполнимым файлам kde, каковыми при ручной сборке по умолчанию являются:

...
-l /usr/local/kde/bin
-l /usr/local/qt/bin
...
-l /usr/local/qt/lib
-l /usr/local/kde/lib

иначе вся затея теряет смысл. Пользователям GNOME умолчальные пути к своим либам и бинарникам предлагается определить в виде самостоятельного упражнения…

Теперь можно, наконец, запустить процесс предварительного связывания. Делается это так:

prelink -avfmR

Смысл опций команды следующий: -a предписывает применить прелинкинг ко всем исполняемым файлам; -v — выводит подробную информацию о ходе процесса — необходимость этого я покажу парой строк ниже; -f — директива применять прелинкинг повторно, даже если эта процедура уже была выполнена (необходимость этого также будет обоснована чуть позже); -m и -R — опции, страхующие от ошибок нехватки памяти и переполнения буфера (подробности, как обычно, — на man prelink).

После этого начинается собственно предварительное связывание, — процесс во времени длящийся, хотя и не столь уж долго — моя машина управляется с ним за время, затрачиваемое на выкуривание сигареты). Однако в ходе его может начаться самый охмуреж: вывод сообщений (обеспечиваемый нам опцией -v, так что без нее не обойтись), что программа имя_рек собрана с опцией non-fPIC и потому прелинкована быть не может. У меня в дистрибутиве CRUX, установленном из прекомпилировыанных пакетов, в этот черный список попали все приложения KDE и некоторая, зато самая тяжелая, часть Иксовых. То есть именно те софтины, ради ускорения которых все и затевалось.

Выход тут, к сожалению, один, простой, но занудный: пересобрать все, что попало в список. И тут уж, разумеется, впредь не забывать, что любая программа, в выводе ./configure --help которой отмечается доступность опции --with-pic, должна конфигурироваться именно с ее участием (флаг -fPIC можно добавить в и список значений переменной CFLAGS). А после этого — повторить процедуру прелинкинга, уже — с неизменно превосходным результатом.

Да, пора поговорить о том, насколько этот результат превосходен (и стоит ли игра свеч вообще). Должен заметить, что первый мой опыт был несколько обескураживающим. Конечно, скорость первой загрузки приложений увеличилась — но далеко не в той степени, как было обещано на gentoo.org (а там угрожали примерно 50-процентным ускорением загрузки KDE и ее приложений). Однако повторение эксперимента (уже на базе CRUX) положение существенно выправило.

Я очень люблю некоторые KDE-приложения — в частности, konqueror, который отношу к числу лучших софтин всех времен и народов, и по долгу службы вынужден использовать некоторые KDE-приложения, аналогов в мире Open Sources не имеющие (например, kdem — программу визуализации данных цифровой картографии в одноименном формате). Так вот, меня всегда страшно раздражало то, как долго эти считанные приложения загружаются — ибо вслед за собой затягивают в память чуть не всю Qt с kdelibs (благо, еще не коран с шариатом, книгой тариката и всеми другими книгами).

Это было до прелинкинга. Что же стало после? На загрузку konqueror уходят неуловимые мгновения, kmail — аналогично, koffice — с OpenOffice даже смешно сравнивать, и так далее. Для пробы запустил саму KDE — время загрузки сопоставимо с оконными менеджерами средней степени тяжести (типа WindowMaker). Короче говоря, используйте прелинкинг — и будет вам счастье…

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

На использование предварительного связывания накладываются некоторые ограничения. Так, после выполнения тотального прелинкинга могут отказаться работать программы, статически связанные с прелинкованными библиотеками. Благо, в нормальной Linux-системе таких или крайне мало, или нет вообще. И к тому же отказ их — отнюдь не обязателен. Так, я использую обычно текстовый редактор Nedit, статически слинкованный с библиотекой Lesstif. Так вот, хотя мой Lesstif перечислен в списке путей прелинкинга, никаких аномалий в работе Nedit’а не обнаруживается — он функционирует вполне справно.

Далее, предварительное связывание не может распространяться на бинарные файлы проприетарного происхождения, для которых исходники недоступны. Однако это обходится совсем просто: достаточно проследить, чтобы такие программы (а у меня в их числе — единственный RealPlayer, да и то лишь потому, что ни лучшего Алмазова, ни Алешковского не могу найти в человеческом формате) устанавливались бы в каталог /opt (что, к слову сказать, они обычно и делают). А каталог этот, понятно, не включать в описание путей в файле /etc/prelink.conf.

И, наконец, существуют программы, которые просто принципиально не желают подвергаться предварительному связывания (по крайней мере, на данном этапе софтостроения). В их числе — все, имеющие отношение к эмулятору wine. Так что ускорить выполнение «подоконных» софтин пока не удастся…