Calculate Forum

Сборка Gentoo с LTO

#1

Как утверждают разработчики компилятора, в GCC 5 стало всё хорошо с LTO. Учитывая, что по не зависящим от меня причинам я давно систему не обновлял, я решил проверить это утверждение на практике и пересобрать мир посредством GCC 5.4 с мультитредом, LTO и gold линкером. Прошлые мои эксперименты с LTO на GCC 4.7 были признаны неудовлетворительными: большое количество пакетов не собирались, а из тех, которые формально собрались, какая-то часть (непонятно какая) собиралась по факту без lto благодаря дефолтному fat-objects, который позволял в случае неудачи тихонечко собрать пакет с кодом, сгенерённым на этапе компиляции безо всякого lto. На графит я решил не заморачиваться: выигрыш сомнительный, а стабильность сильно под вопросом.

Дисклеймер: автор не несёт ответственности за сломанную вами систему при попытке следовать этому руководству. Я попытался сделать его максимально подробным, но это всего лишь примерное описание шагов, который не заменит понимания того, как работает система и что надо делать. Без этого понимания невозможно будет решить возникшие проблемы – а они будут! Используйте это как опорный конспект – на свой страх и риск.

Итак,

  1. Собираем GCC 5.4 из нестабильной ветки,

    root # echo “sys-devel/gcc” >> /etc/portage/package.accept_keywords
    root # emerge -u sys-devel/gcc
    root # gcc-config -l

    [1] x86_64-pc-linux-gnu-4.9.4 *
    [2] x86_64-pc-linux-gnu-5.4.0

2. Переключаем компилятор командой

root # gcc-config 2 

3. Пересобираем libtools и устанавливаем binutils посвежее (9999 не обязательно, достаточно размаскировать нестабильную версию)

root # env-update && source /etc/profile
root # emerge --oneshot libtool
root # echo "sys-devel/binutils" >> /etc/portage/package.accept_keywords
root # emerge --oneshot binutils

4. Переключаем линковщик

root # binutils-config --linker ld.gold

Чтобы подхватывались нужные ar nm и ranlib, необходимо создать симлинк

root # ln -s /usr/libexec/gcc/x86_64-pc-linux-gnu/5.4.0/liblto_plugin.so.0.0.0 /usr/x86_64-pc-linux-gnu/binutils-bin/lib/bfd-plugins

Некоторые устаревшие руководства предлагают поместить в make.conf “обёртки” для этих программ, на настоящий момент этого делать не нужно, достаточно прописать симлинк.

Далее руководство по gcc советует

5. обновить зависимые пакеты

root # revdep-rebuild --library 'libstdc\+\+.so.6' -- --exclude gcc

(подробности тут и тут)

В моём случае, учитывая, что я изменил режим компиляции и накопил большое количество обновлений, я попросту пересобрал мир. Мои флаги под AMD Athlon™ X4 760K:

CFLAGS="-march=native -pipe -Wno-all -O3 -flto=4 -fuse-ld=gold -mprefer-avx128 -mvzeroupper" 
CXXFLAGS="${CFLAGS}"
LDFLAGS="-Wl,-O3,--sort-common,--hash-style=gnu,--as-needed -fuse-linker-plugin -s ${CGLAGS}"

Дальше процесс таков:

6. Создаём в директории /etc/portage/env файл с названием no-lto.conf следующего содержания:

CFLAGS="${CFLAGS} -fno-lto -fno-use-linker-plugin"
CXXFLAGS="${CXXFLAGS} -fno-lto -fno-use-linker-plugin"
LDFLAGS="${LDFLAGS} -fno-lto -fno-use-linker-plugin"

Кроме него, нам понадобятся файлы no-lto-bfd.conf

CFLAGS="${CFLAGS} -fno-lto -fno-use-linker-plugin -fuse-ld=bfd"
CXXFLAGS="${CXXFLAGS} -fno-lto -fno-use-linker-plugin -fuse-ld=bfd"
LDFLAGS="${LDFLAGS} -fno-lto -fno-use-linker-plugin"

vanilla-O3.conf

CFLAGS="-march=native -O3 -pipe -Wno-all"
CXXFLAGS="${CFLAGS}"
LDFLAGS="${LDFLAGS} -fno-lto -fno-use-linker-plugin"

gold.conf

EXTRA_ECONF="--enable-gold=default"

Создаём файл /etc/portage/package.env следующего содержания:

sys-devel/binutils gold.conf

нужный для того, чтобы при пересборке binutils не съехал линковщик. Далее настало время

7. собрать мир

emerge -e world

Если какой-то пакет не собирается, emerge вылетит с ошибкой. Смотрим, какой файл не собрался, и добавляем строку в наш package.env

<категория/пакет> no-lto.conf

У меня получился следующий файл

sys-libs/zlib no-lto.conf
dev-lang/perl no-lto.conf
x11-base/xorg-server no-lto.conf
sys-libs/ncurses no-lto.conf
sys-devel/gettext no-lto.conf
dev-qt/qtscript no-lto.conf
dev-lang/spidermonkey no-lto.conf
media-libs/x264 no-lto.conf
dev-python/notify-python no-lto.conf
app-text/rarian no-lto.conf
x11-libs/wxGTK:2.8 no-lto.conf
media-video/mediainfo no-lto.conf
media-libs/alsa-lib no-lto.conf
app-emulation/qemu no-lto.conf

Состав этого файла зависит от архитектуры, версии компилятора и пакетов и от состава установленных пакетов и приводится в качестве примера.

8. Особые случаи. В большинстве случаев предложенный подход сработает, но не всегда. Есть пакеты, которые собираются только с минимальным набором флагов, в моём случае это

dev-lang/python vanilla-O3.conf
sys-boot/grub vanilla-O3.conf

Есть пакеты, которые собираются, но не работают, у меня это

#сегфолтится с lto
media-sound/timidity++ no-lto.conf 

Есть пакеты, которые не собираются с gold-линковщиком

# требуют bfd
app-text/tesseract no-lto-bfd.conf 
app-i18n/poedit no-lto-bfd.conf
media-gfx/enblend no-lto-bfd.conf
sys-apps/gsmartcontrol no-lto-bfd.conf
media-gfx/rawtherapee no-lto-bfd.conf

Надо отметить, что при чтении elog файлов видно, что некоторые пакеты переключают линковщик сами и не требуют специальных манипуляций. Спасибо мейнтейнерам!

И наконец, самая хитрая категория: библиотеки, которые прекрасно собираются, но при оптимизации из них вычищаются нужные символы (объявленные локально?), из-за чего не собираются другие пакеты (у меня это poedit qemu rawtherapy gsmartcontrol и inkscape. Последний при этом даже собирается с lto, если библиотеки собраны без).

#В этих библиотеках иначе не видны все символы
dev-cpp/gtkmm no-lto.conf
dev-cpp/glibmm no-lto.conf
dev-cpp/atkmm no-lto.conf
dev-cpp/cairomm no-lto.conf
dev-libs/libaio no-lto.conf

Это полный список проблемных пакетов в системе, содержащей порядка 1100 пакетов. На настоящий момент система работает быстро и стабильно, хотя со временем могут выплыть ещё какие-то неожиданности. Так что, если кому-то будет нечего делать на праздники…

#2

В интернете немного написано про LTО оптимизации. Кому они полезны? Пользователям у которых система разбита на множество модулей? Чем-то данные оптимизации полезны тем кто собирают монолит под конкретную систему?

#3

Это не “монолит”, это всего лишь оптимизация кода, отложенная до момента, когда для этого доступно максимум информации. Количество модулей при этом не меняется.
ЛТО полезен всем. Сегодня в линуксе по умолчанию компилятор проводит оптимизацию кода помодульно. Это значит, что он, например, не может выкинуть функцию, которую никто никогда не вызывает, потому что она может вызываться из другого модуля и он об этом не знает. На этапе связывания у него есть вся информация и он может проводить более глубокие оптимизации: выкидывать неиспользуемый код, инлайнить функции из других модулей и т.д. При этом код (теоретически) получается более быстрым и меньшим по размеру. Средний выигрыш порядка 10%, что в масштабе системы уже заметно. Под оффтопиком уже давно компиляторы так работают, ну да там и деньги на разработку другие совсем.
Хотя возможны варианты: например, когда много инлайнов, код может даже распухнуть, но будет очень быстрым, или наоборот – получится чуть медленнее, но сильно меньше. Статистика есть по ссылке в статье. ЛТО хорошо сочетается с -О3, которая генерит пухлый код, который в свою очередь глубже оптимизируется на этапе связывания.
Больше информации тут.

#4

Добрый день!
Спасибо за статью, это первый детальный мануал по которому получилось собрать систему с LTO в кои то веки ))

Единственное как ни пытался но не могу перекомпилить sys-firmware/ipxe (который нужен qemu), валится с этой ошибкой:
make: ***** [arch/x86/Makefile.pcbios:116: bin/usbdisk.bin] Ошибка сегментирования

Не сталкивались с такой бедой?

#5

Добрый!

У меня и qemu и ipxe стоят, ничего не валилось.
Попробуйте внести пакет в список исключений.
Вообще, судя по приведённой вами строке, это make сам валится, попробуйте его пересобрать/обновить.

#6

Привет!
Запилил 2 патча для dev-qt/qtscript-4.8.7 и dev-qt/qtwebkit-4.8.7 чтоб без ошибок с LTO собирались.

/etc/portage/patches/dev-qt/qtscript-4.8.7/qtscript-4.8.7_gcc-flto-fix.patch:

diff --git a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/javascriptcore/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h
index ef77e19..ef3340a 100644
--- a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/javascriptcore/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h
+++ b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h
@@ -1176,7 +1176,7 @@ __asm void cti_op_debug_return(STUB_ARGS_DECLARATION)
 }

 extern "C" EncodedJSValue JITStubThunked_vm_throw(STUB_ARGS_DECLARATION);
-__asm EncodedJSValue cti_vm_throw(STUB_ARGS_DECLARATION)
+__asm EncodedJSValue cti_vm_throw(STUB_ARGS_DECLARATION) REFERENCED_FROM_ASM
 {
     ARM
     IMPORT JITStubThunked_vm_throw
diff --git a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/javascriptcore/JavaScriptCore/jit/JITStubs.h b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/jit/JITStubs.h
index da80133..10e678c 100644
--- a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/javascriptcore/JavaScriptCore/jit/JITStubs.h
+++ b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/jit/JITStubs.h
@@ -316,7 +316,7 @@ extern "C" {
     EncodedJSValue JIT_STUB cti_op_to_primitive(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_typeof(STUB_ARGS_DECLARATION);
     EncodedJSValue JIT_STUB cti_op_urshift(STUB_ARGS_DECLARATION);
-    EncodedJSValue JIT_STUB cti_vm_throw(STUB_ARGS_DECLARATION);
+    EncodedJSValue JIT_STUB cti_vm_throw(STUB_ARGS_DECLARATION) REFERENCED_FROM_ASM;
     EncodedJSValue JIT_STUB cti_to_object(STUB_ARGS_DECLARATION);
     JSObject* JIT_STUB cti_op_construct_JSConstruct(STUB_ARGS_DECLARATION);
     JSObject* JIT_STUB cti_op_new_array(STUB_ARGS_DECLARATION);
diff --git a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/javascriptcore/JavaScriptCore/wtf/Platform.h b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/wtf/Platform.h
index 605a0ed..a7bd5dd 100644
--- a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/javascriptcore/JavaScriptCore/wtf/Platform.h
+++ b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/javascriptcore/JavaScriptCore/wtf/Platform.h
@@ -1088,6 +1088,14 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */
 #define WARN_UNUSED_RETURN
 #endif

+#ifndef REFERENCED_FROM_ASM
+#if COMPILER(GCC)
+#define REFERENCED_FROM_ASM __attribute__((used))
+#else
+#define REFERENCED_FROM_ASM
+#endif
+#endif
+
 #if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(GTK)))
 #define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1
 #endif

/etc/portage/patches/dev-qt/qtwebkit-4.8.7/qtwebkit-4.8.7_gcc-flto-fix.patch:

diff --git a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/webkit/Source/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h
index 783586d..7943311 100644
--- a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/webkit/Source/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h
+++ b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/generated/GeneratedJITStubs_RVCT.h
@@ -1264,7 +1264,7 @@ __asm void cti_op_debug(STUB_ARGS_DECLARATION)
 }

 extern "C" void* JITStubThunked_vm_throw(STUB_ARGS_DECLARATION);
-__asm void* cti_vm_throw(STUB_ARGS_DECLARATION)
+__asm void* cti_vm_throw(STUB_ARGS_DECLARATION) REFERENCED_FROM_ASM
 {
     PRESERVE8
     IMPORT JITStubThunked_vm_throw
diff --git a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/webkit/Source/JavaScriptCore/jit/JITStubs.h b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/jit/JITStubs.h
index 624b6be..a7399f5 100644
--- a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/webkit/Source/JavaScriptCore/jit/JITStubs.h
+++ b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/jit/JITStubs.h
@@ -430,7 +430,7 @@ extern "C" {
     void* JIT_STUB cti_register_file_check(STUB_ARGS_DECLARATION);
     void* JIT_STUB cti_vm_lazyLinkCall(STUB_ARGS_DECLARATION);
     void* JIT_STUB cti_vm_lazyLinkConstruct(STUB_ARGS_DECLARATION);
-    void* JIT_STUB cti_vm_throw(STUB_ARGS_DECLARATION);
+    void* JIT_STUB cti_vm_throw(STUB_ARGS_DECLARATION) REFERENCED_FROM_ASM;
 } // extern "C"

 } // namespace JSC
diff --git a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h
index e8b03be..d0d3419 100644
--- a/qt-everywhere-opensource-src-4.8.7-orig/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h
+++ b/qt-everywhere-opensource-src-4.8.7/src/3rdparty/webkit/Source/JavaScriptCore/wtf/Platform.h
@@ -1181,6 +1181,14 @@
 #define WARN_UNUSED_RETURN
 #endif

+#ifndef REFERENCED_FROM_ASM
+#if COMPILER(GCC)
+#define REFERENCED_FROM_ASM __attribute__((used))
+#else
+#define REFERENCED_FROM_ASM
+#endif
+#endif
+
 #if !ENABLE(NETSCAPE_PLUGIN_API) || (ENABLE(NETSCAPE_PLUGIN_API) && ((OS(UNIX) && (PLATFORM(QT) || PLATFORM(WX))) || PLATFORM(GTK)))
 #define ENABLE_PLUGIN_PACKAGE_SIMPLE_HASH 1
 #endif
Mastodon Mastodon