- PVSM.RU - https://www.pvsm.ru -

Сборка ядра Linux 5.12.10 c LLVM 12 + Clang и LTO оптимизацией

Сборка ядра Linux 5.12.10 c LLVM 12 + Clang и LTO оптимизацией - 1


Технический прогресс не стоит на месте, появляются новые компьютерные архитектуры, компиляторы становятся умнее и генерируют более быстрый машинный код. Современные задачи требуют все более креативного и эффективного решения. В данной статье пойдет речь, на мой взгляд, про один из самых прогрессивных тулчейнов LLVM и компиляторы на его основе Clang и Clang++, для языков программирования С и C++ соответственно. Хоть GCC — конкурент Clang, может агрессивнее оптимизировать циклы и рекурсию [1], Clang дает на выходе более корректный машинный код, и чаще всего не ломает поведение приложений. Плюс оптимизация программ не заканчивается только оптимизацией циклов, поэтому Clang местами дает лучшую производительность. В GCC же за счет переоптимизации вероятность получить unpredictable behavior значительно выше. По этой причине на многих ресурсах не рекомендуют использовать -O3 и LTO(Link Time Optimization) оптимизации для сборки программ. Плюс в случае агрессивной оптимизации, размер исполняемых файлов может сильно увеличиться и программы на практике будут работать даже медленнее. Поэтому мы остановились на Clang не просто так и опции компиляции -O3 и LTO работают в нем более корректно. Плюс современные компиляторы более зрелые, и сейчас уже нет тех детских болячек переоптимизации и LTO.

Что меня побудило написать эту статью? — В первую очередь это несколько фактов:

  1. Впервые прочел про сборку ядра Linux с LTO оптимизацией и Clang из новостей, где упоминалась компания Google. Она использует Clang и LTO оптимизацию для сборки ядра Linux и получения лучшей производительности. Компания Google для меня является синонимом инноваций, лучших программистов в мире и поэтому для меня ее опыт является самым авторитетным. Плюс она привнесла очень много в развитие open source, и ее наработками пользуются тысячи компаний во всем мире.
  2. Хоть компания Google начала использовать Clang и LTO оптимизацию раньше, только с выходом ядра Linux 5.12.6 и 5.12.7 было закрыто большое количество багов, и сборка ядра c LTO оптимизаций стала доступна многим. До этого при сборке ядра с LTO оптимизацией многие драйвера давали сбой.
  3. Мною уже протестирована работа ядра с LTO на Ryzen 9 3900x + AMD Radeon 5700 XT. Плюс уже давно использую LLVM 12 и Clang для сборки системных программ. Инструментарий LLVM12 и Clang стали основными в моей системе по причине лучшей поддержки моего процессора и нужные мне программы работают быстрее при сборке с помощью Clang. Для программистов Clang дает лучший контроль ошибок, оптимизации и unpredictable behavior. -fdebug-macro, -fsanitize=address, -fsanitize=memory, -fsanitize=undefined, -fsanitize=thread, -fsanitize=cfi, -fstack-protector, -fstack-protector-strong, -fstack-protector-all, -Rpass=inline, -Rpass=unroll, -Rpass=loop-vectorize, -Rpass-missed=loop-vectorize, -Rpass-analysis=loop-vectorize и т.д.
  4. Данная возможность толком нигде не была описана в связи с п.2 и есть подводные моменты, которые будут рассмотрены в данной статье.

В этой статье будет описана сборка ядра Linux 5.12.10 c LLVM 12 + Clang и LTO оптимизацией. Но так как статья получилась бы короткой, то так же бонусом будет рассмотрен вопрос как сделать утилиты LLVM 12 и Clang сборочным инструментарием по умолчанию, и какие программы и библиотеки имеет смысл собрать вручную, чтобы получить лучший отклик и производительность от системы. GCC имеет более лояльную лицензию на использование, и поэтому он установлен во многих дистрибутивах по умолчанию.

Так как в новом ядре фиксится немалое количество багов для работы с моим оборудованием(Ryzen 9 3900x + AMD Radeon 5700 XT) будет рассмотрен вопрос автоматизации сборки и установки нового ядра, чтобы это сильно не отвлекало и занимало минимум времени. Думаю многим это будет полезно. Будет рассмотрен принцип работы моего сборочного скрипта. Все действия будут проводиться в Arch Linux. Если статья будет хорошо оценена, то она станет вводной частью в серию статей про оптимизацию Linux, где будут рассмотрены внутренние механизмы ОС, и как оптимизировать их работу, будут рассмотрены вредные советы и ошибки оптимизации, и будет дан ответ на вопрос оптимизации системы «Что для русского хорошо, то для немца смерть!».

Хоть тема оптимизации описывалась многократно, не мало где дают вредные советы, и некоторые механизмы ОС описаны с ошибками. Чаще всего это происходит из-за сложностей перевода или минимальной документации в интернете к компонентам ядра Linux. Где-то информация вовсе устарела. Плюс некоторые вещи понимают программисты, но не понимают системные администраторы, и наоборот. Изначально после установки Linux работает относительно медленно, но благодаря оптимизации и гибкой настройке, можно добиться более высокой производительности и значительно улучшить отклик системы. Arch Linux у меня используется как основная система, и отклик системы, производительность лучше, чем в Windows 10.

Внимание, автор статьи не несет ответственность за причиненный вред в следствии использования данной статьи! Все действия вы выполняете на свой страх и риск! Все действия должны выполнять только профессионалы!

▍Немного теории

LTO или Link Time Optimization это оптимизация на этапе линковки(компоновки). Чтобы понять, что такое LTO рассмотрим как работают компиляторы. В большинстве компиляторов используется двух этапная модель: этап компиляции и этап линковки.

На этапе компиляции:

— Парсятся исходные тексты программ, строится ASTАбстрактное Синтаксическое Дерево [2].

  • Оптимизируется Абстрактное Синтаксическое Дерево. Оптимизируются циклы, удаляется мертвый код, результат которого нигде не используется. Раскрываются выражения, например 2+5 можно заменить на 7, чтобы при работе приложения не вычислять его значение каждый раз и тем самым сделать его быстрее и т.д.
  • Оптимизированное дерево может быть преобразовано в машинный псевдокод понятный компилятору. Псевдокод используется для дополнительной оптимизации, упрощает разработку универсального компилятора для разных архитектур процессора, например для x86-64 и ARMv7. Так же как ASM листинг, этот псевдокод еще используется, чтобы понять, как компилятор генерирует машинный код, и служит для понимания работы компилятора, поиска ошибок, например, ошибок оптимизации и unpredictable behavior. Стоит заметить этот этап не является обязательным и в некоторых компиляторах отсутствует.
  • Происходит векторизация. Векторизация [3] ,Automatic Vectorization [4], SIMD [5]
  • Генерируется объектный файл. Объектный файл содержит в себе машинный код для компьютера, и специальные служебные структуры, в которых все еще есть неизвестные адреса функций и данных, поэтому этот файл все еще не может быть запущен на исполнение. Чтобы разрешить неизвестные адреса, был добавлен этап линковки.

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

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

На этапе линковки:

  • Происходит подстановка адресов
  • Добавляются дополнительных данных для работы программы, например ресурсы
  • Происходит сборка всех объектных файлов в конечный исполняемый файл или распространяемую библиотеку, которая может быть использована в других программах

Понимая это мы можем понять, что LTO оптимизация это дополнительная оптимизация исполняемых файлов, которая не может быть произведена на этапе компиляции и происходит на этапе линковки.

В Clang используется два вида LTO Оптимизации: Full LTO и Thin LTO. Full LTO — это классическая реализация LTO оптимизации, которая обрабатывает конечный исполняемый файл за раз целиком и использует много оперативной памяти. Отсюда эта оптимизация занимает много времени, но дает на выходе самый быстрый код. Thin LTO — это развитие LTO оптимизации, в которой нет оптимизации всего файла целиком, а вместо этого вместе с объектными файлами записывают дополнительные метаданные, и LTO оптимизатор работает с этими данными, что дает более высокую скорость получения оптимизированного исполняемого файла (скорость сравнима с линковкой файла без LTO оптимизации) и код сравнимый или чуть уступающий в производительности Full LTO. Но самое главное Full LTO может значительно увеличить размер файла, и код наоборот может из-за этого работать медленнее. Thin LTO лишен этого недостатка и в некоторых приложениях на практике мы можем получить лучшую производительность! Поэтому наш выбор будет сборка ядра Linux с Thin LTO.

Дополнительная информация:

▍Установка LLVM 12 и Clang

Поставить llvm и clang можно выполнив в консоли под root команду:

pacman -Syu base-devel llvm clang lld vim

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

Прошлая версия

На момент написания статьи, в дистрибутиве Arch Linux используются LLVM и Clang версии 11. А LLVM и Clang версии 12 находятся в staging репозитории Arch Linux [LLVM](https://archlinux.org/packages/staging/x86_64/llvm/). Staging репозиторий это репозиторий, где находятся версии пакетов, которые ломают приложения, зависящие от прошлой версии. Он используется для компиляции всех зависящих программ, и когда все они будут собраны, все пакеты за раз переходит в общий репозиторий. Например, в Arch Linux от LLVM и Clang версии 11 зависят blender, rust и qt creator и т.д. Если мы поставим LLVM и Clang версии 12, то они перестанут работать.
Upd. Пакет уже перешел в основной репозиторий. Так как мною одним из первых была произведена миграция на LLVM и Clang 12, то было придумано простое решение, создать пакет [llvm11-libs](https://aur.archlinux.org/packages/llvm11-libs-bin/) с необходимыми библиотеками для обратной совместимости, который позволяет оставить зависимые программы рабочими. Но данный пакет работает только с моим сборочным пакетом [llvm12-git](https://aur.archlinux.org/packages/llvm12-git/). Поэтому мы будем собирать LLVM и Clang 12 из исходников. Но вы можете дождаться, когда LLVM и Clang 12 появятся в основном репозитории Arch Linux или использовать 11 версию. Лично предпочитают новые версии ПО, и LLVM и Clang 12 лучше поддерживают мой процессор Ryzen 9 3900X. Плюс git версия закрыла часть багов компилятора и даже стабильнее релиза. Релизный архив с официального сайта у меня не проходит больше тестов при сборке чем git версия. Не стоит пугаться того, что часть тестов компилятор провалил, там нет критических багов для x84-64 архитектуры, и большая часть затрагивают другие компоненты, например openmp и lldb. За очень долгое время тестирования llvm и clang 12 мною не было замечено ни одного бага влияющего на работу системы. Стоит заметить, на данный момент 13 версия является очень сырой и нам не подходит!

Поставим llvm и clang 11 версии(Если 12 версия появилась в основном репозитории, то поставится 12я версия) можно выполнив в консоли под root команду:

pacman -Syu base-devel llvm clang lld libclc vim

Обновить Arch Linux и поставить новые версии программ можно командой(это будет полезно тем кто будет ждать официального выхода 12 версии, думаю это произойдет уже через пару дней):

pacman -Syu

Кто остановился на этом варианте можно пропустить следующий пункт. Для остальных, будет дана инструкция по установке моего пакета.

Cборка LLVM 12 из Arch User Repository

Для сборки нам понадобиться git и нам надо будет собрать программу yay.

Поставим необходимые зависимости, для этого нам будут нужны права root: pacman -Syu base-devel git go vim

Если вы хотите собрать llvm 12 с помощью clang 11, то надо поставить еще их: pacman -S llvm clang

Отредактируем конфигурационный файл сборщика пакетов makepkg в Arch Linux и увеличим количество потоков для сборки программ. Это ускорит скорость сборки. Под root выполним: vim /etc/makepkg.conf

Найдем строки MAKEFLAGS и NINJAFLAGS. Нажмем латинскую букву A. Нам после -j надо указать количество потоков для сборки. Рекомендуется ставить ваше количество ядер или потоков процессора, если ядер 4, то ставим 4 или 8. У меня это 20, 12 ядер — 24 потока, 4 остаются запасными для других задач. Или используем автоматическое определение $(nproc).

В итоге получим:

MAKEFLAGS="-j20"
NINJAFLAGS="-j20"

или

MAKEFLAGS="-j$(nproc)"
NINJAFLAGS="-j$(nproc)"

Нажмем ESC, дальше SHIFT + :(буква Ж). Внизу появится : — строка для ввода команд, вводим wq. w — write, записать изменения в файл. q — quit, выйти из vim. q! выход из vim без сохранения файла. Кому сложно разобраться с vim, в Linux есть замечательная программа, называется она vimtutor. Если у вас настроена правильно локаль, то vimtutor будет на русском, запустить его можно командой vimtutor. Стоит заметить, вопреки распространенному мнению, обучение у вас не займет много времени. Обычно новичков пугают мифом: vi и vim люди изучают очень долго, и осилить их могут только единицы. На самом деле это не так и там нет ничего сложного.

Под обычным пользователем клонируем репозиторий yay, собираем и устанавливаем:

git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -cfi

Импортирует открытый gpg ключ, он необходим для проверки подписи llvm12-git:

gpg --keyserver pgp.mit.edu --recv-keys 33ED753E14757D79FA17E57DC4C1F715B2B66B95

Поставим LLVM 12 и библиотеки совместимости с 11 версией. Стоит заметить, мой пакет LLVM 12 уже содержит все необходимые утилиты, включая Clang и LLD и их не надо ставить отдельно. Под обычным пользователем выполним команду: yay -Syu llvm12-git. Если llvm 12 есть в официальном репозитории, то llvm11-libs-bin не нужно ставить. Команда yay задаст вам несколько вопросов, нажмите Enter в ответ на все. Сборщик LLVM задаст 3 вопроса:

  • Build with clang and llvm toolchain? — Собрать с помощью llvm и clang? Отвечаем Y или Enter если да, и N если нет. Рекомендую собирать LLVM с помощью Clang.
  • Skip build tests? Пропустить сборку тестов? Отвечаем Y или Enter. Так как во время сборки, не все тесты проходят проверку, то сборка будет прекращена. Поэтому мы пропускаем сборку тестов, и на самом деле сборка будет идти даже быстрее.
  • Skip build documentation? Пропустить сборку документации? Отвечаем Y или Enter если да, и N если нет. Если вам не нужна документация, то можно пропустить, это ускорит сборку. Лучше читать документацию на официальном сайте, это удобнее.
  • Skip build OCaml and Go bindings? Пропустить сборку OCaml и Go биндингов? Отвечаем Y или Enter если да, и N если нет. Для большинства ответ Y и их сборку можно смело пропустить в угоду скорости сборки. Для тех кому они нужны, а это очень маленькое количество людей могут ответить N.

Сборка может занять от 20 минут до пары часов. Ждете и в конце отвечаете Y на вопрос: хотите ли вы поставить собранные пакеты?

После установка LLVM надо собрать libclc12-git yay -S libclc12-git. libclc необходим для компиляции opencl и для сборки mesa.

▍Делаем LLVM и Clang сборочным тулчейном по умолчанию в Arch Linux

Большинство программ в Arch Linux собираются с помощью команды makepkg: man makepkg и PKGBUILD файлов. Поэтому в первую очередь внесем изменения в конфигурационный файл /etc/makepkg.conf. Выполним под root в консоли команду: vim /etc/makepkg.conf. Перейдем к строке CHOST="x86_64-pc-linux-gnu" поставим курсор на следующей пустой строке и нажмем латинскую букву «A», и вставим после строки:

export CC=clang
export CXX=clang++
export LD=ld.lld
export CC_LD=lld
export CXX_LD=lld
export AR=llvm-ar
export NM=llvm-nm
export STRIP=llvm-strip
export OBJCOPY=llvm-objcopy
export OBJDUMP=llvm-objdump
export READELF=llvm-readelf
export RANLIB=llvm-ranlib
export HOSTCC=clang
export HOSTCXX=clang++
export HOSTAR=llvm-ar
export HOSTLD=ld.lld

Дальше заменим строки CPPFLAGS, CXXFLAGS, LDFLAGS на содержимое ниже:

CFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"
CXXFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"
LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"

Если вкратце мы используем -O2 оптимизацию для всех программ, -fstack-protector-strong используем улучшенную защиту стека, что снижает вероятность потенциально опасных ошибок при работе со стеком в программах, она же включена у меня в ядре. Плюс на моем процессоре при сборке с Clang с -fstack-protector-strong код при работе с целыми числами работает чуть быстрее, при работе с числами с плавающей запятой есть небольшой оверхед. В GCC наоборот есть более заметный оверхед и производительность снижается. -march=native есть смысл заменить на ваш, у меня это -march=znver2 gcc.gnu.org/onlinedocs/gcc/x86-Options.html [8].

Изменим количество потоков в MAKEFLAGS и NINJAFLAGS для сборки программ. Это помогает ускорить сборку программ. После -j надо указать количество потоков для сборки. Рекомендуется ставить ваше количество ядер или потоков процессора, если ядер 4, то ставим 4 или 8. У меня это 20, 12 ядер, 24 потока, 4 остаются запасными для других задач. Или используем автоматическое определение $(nproc).

В итоге получим:

MAKEFLAGS="-j20"
NINJAFLAGS="-j20"

или

MAKEFLAGS="-j$(nproc)"
NINJAFLAGS="-j$(nproc)"

Из DEBUG_CFLAGS и DEBUG_CXXFLAGS надо удалить -fvar-tracking-assignments. LLVM не поддерживает данный параметр.

Файл должен будет принять примерно такой вид:

CARCH="x86_64"
CHOST="x86_64-pc-linux-gnu"

CARCH="x86_64"
CHOST="x86_64-pc-linux-gnu"
#-- Compiler and Linker Flags
export CC=clang
export CXX=clang++
export LD=ld.lld
export CC_LD=lld
export CXX_LD=lld
export AR=llvm-ar
export NM=llvm-nm
export STRIP=llvm-strip
export OBJCOPY=llvm-objcopy
export OBJDUMP=llvm-objdump
export READELF=llvm-readelf
export RANLIB=llvm-ranlib
export HOSTCC=clang
export HOSTCXX=clang++
export HOSTAR=llvm-ar
export HOSTLD=ld.lld

CPPFLAGS="-D_FORTIFY_SOURCE=2"
CFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"
CXXFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"
LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"
RUSTFLAGS="-C opt-level=2"
#-- Make Flags: change this for DistCC/SMP systems
MAKEFLAGS="-j20"
NINJAFLAGS="-j20"
#-- Debugging flags
DEBUG_CFLAGS="-g"
DEBUG_CXXFLAGS="-g"
#DEBUG_CFLAGS="-g -fvar-tracking-assignments"
#DEBUG_CXXFLAGS="-g -fvar-tracking-assignments"
#DEBUG_RUSTFLAGS="-C debuginfo=2"

Нажмем ESC, дальше SHIFT + :(буква Ж). Внизу появится: — строка для ввода команд, вводим wq. w — write, записать изменения в файл. q — quit, выйти из vim. q! выход из vim без сохранения файла. Кому сложно разобраться с vim, в Linux есть замечательная программа, называется она vimtutor. Если у вас настроена правильно локаль, то vimtutor будет на русском, запустить его можно командой `vimtutor`. Стоит заметить, вопреки распространенному мнению, обучение у вас не займет много времени. Обычно новичков пугают мифом: vi и vim люди изучают очень долго, и осилить их могут только единицы. На самом деле это не так и там нет ничего сложного.

Следующим этапом можно добавить настройки в файл .bashrc текущего пользователя. Не root, сборка программ под root очень плохая идея! Это относительно вредный совет и с помощью clang будут собираться все программы! Поэтому делайте это только если хорошо понимаете зачем это вам. Это можно сделать командой:

cat << 'EOF' >> "${HOME}/.bashrc"

export CARCH="x86_64"
export CHOST="x86_64-pc-linux-gnu"
export CC=clang
export CXX=clang++
export LD=ld.lld
export CC_LD=lld
export CXX_LD=lld
export AR=llvm-ar
export NM=llvm-nm
export STRIP=llvm-strip
export OBJCOPY=llvm-objcopy
export OBJDUMP=llvm-objdump
export READELF=llvm-readelf
export RANLIB=llvm-ranlib
export HOSTCC=clang
export HOSTCXX=clang++
export HOSTAR=llvm-ar
export HOSTLD=ld.lld

export CPPFLAGS="-D_FORTIFY_SOURCE=2"
export CFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"
export CXXFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"
export LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"
export RUSTFLAGS="-C opt-level=2"
export MAKEFLAGS="-j20"
export NINJAFLAGS="-j20"
export DEBUG_CFLAGS="-g"
export DEBUG_CXXFLAGS="-g"

EOF

▍Список системных библиотек и программ которые стоит собирать вручную

Внимание, сборка всех программ и все консольные команды надо выполнять под обычным пользователем, перед установкой у вас попросит пароль root. Сборка всех библиотек и программ из списка не занимает много времени. Все кроме Mesa у меня собирается в районе 1 минуты. Список дан в той в последовательности в которой рекомендуется сборка! К примеру от zlib-ng и zstd зависит Mesa, а от Mesa зависит xorg-server.

Самое первое, что надо сделать в Arch Linux это заменить zlib на zlib-ng. Это дает хороший выигрыш производительности в приложениях, которые зависят от zlib. Больше всего это заметно на веб браузерах и веб серверах, которые используют gzip сжатие для передачи данных. На высоко нагруженных серверах это дает очень значительную прибавку к производительности. Сборка довольно быстрая. Поставить можно командой(под обычным пользователем): yay -Syu zlib-ng. На вопрос хотите ли вы удалить zlib отвечайте Y. Не бойтесь библиотеки полностью взаимозаменяемы, и ничего не сломается!

Дальше у нас идет zstd это вторая по популярности библиотека используемая в ядре и в программах для сжатия данных. Поэтому имеет смысл собрать так же ее. Чтобы собрать, вам нужно скопировать содержимое zstd [9], создать директорию, например zstd, а в ней создать файл PKGBUILD и в него вставить содержимое по ссылке. Дальше в консоли перейти в директорию содержащую PKGBUILD, выполнить команду makepkg -cfi .

libjpeg-turbo — Библиотека для работы c jpeg файлами. Ее очень часто используют браузеры и программы рабочего стола. libjpeg-turbo собранный с clang дает у меня лучшую производительность. Действия такие же, как в zstd. Создать директорию, и вставить в файл PKGBUILD содержимое по ссылке libjpeg-turbo [10]. Дальше в консоли перейдите в директорию содержащую PKGBUILD, выполнить команду makepkg -cfi.

libpng — Библиотека для работы с PNG файлами. По сборке и установке все то же самое. libpng [11]. Для сборки вам понадобится патч: 72fa126446460347a504f3d9b90f24aed1365595.patch [12], его надо положить в одну директорию с файлом PKGBUILD. Для сборки надо внести изменения в PKGBUILD, заменить source и sha256sums на строки ниже, и добавить функцию prepare.

source=("https://downloads.sourceforge.net/sourceforge/$pkgname/$pkgname-$pkgver.tar.xz"
  "72fa126446460347a504f3d9b90f24aed1365595.patch")
sha256sums=('505e70834d35383537b6491e7ae8641f1a4bed1876dbfe361201fc80868d88ca'
  '84298548e43976265f414c53dfda1b035882f2bdcacb96ed1bc0a795e430e6a8')

prepare() {
  cd $pkgname-$pkgver
  patch --forward --strip=1 --input="${srcdir:?}/72fa126446460347a504f3d9b90f24aed1365595.patch"
}

Mesa — это святой грааль для всех графических приложений. Стоит собирать всегда вручную, дает хорошую прибавку в десктоп приложениях, улучшается отклик рабочего стола. Одно время сидел на git версии, чтобы получить лучшую поддержку новых видеокарт AMD. Вот мой PKGBUILD [13] оптимизированный для сборки с помощью Clang.

Для сборки вам надо отредактировать файл mesa.conf и установить необходимые вам драйвера dri, gallium, vulkan для сборки. У меня сборка только под новые видеокарты AMD. Подглядеть можно тут: Mesa OpenGL [14], mesa-git package [15], Mesa Documentation [16]. При выходе новой версии Mesa не забудьте сменить 21.1.2 на новую версию. А после смены версии обновите контрольные суммы файлов, выполнив в директории с PKGBUILD команду updpkgsums.

xorg-server — X сервер с которым взаимодействуют почти все среды рабочего стола. Сборка дает заметное улучшение отклика рабочего стола. Сборка такая же через mapkepkg -cfi. Скачать необходимые файлы для сборки можно тут: xorg-server [17] Сборочный пакет немного кривой и собирает пакет без оптимизаций. Поэтому его надо пропатчить. Для это после строки arch-meson ${pkgbase}-$pkgver build надо добавить строки:

  -D debug=false 
  -D optimization=2 
  -D b_ndebug=true 
  -D b_lto=true 
  -D b_lto_mode=thin 
  -D b_pie=true 

Полный список критических важных программ влияющих на производительность системы вы можете посмотреть в моём github репозитории arch-packages [18]. Список был создан с помощью системного профилировщика perf. Все сборочные файлы оптимизированы для сборки с помощью llvm и сборка полностью автоматизирована. На моем ryzen 9 3900x сборка всего занимает около 20 минут. Единственный пакет который невозможно собрать с помощью clang и llvm это glibc. Его надо собирать вручную, и с оптимизацией -march= под ваш процессор, это самая часто вызываемая библиотека. Сборку glibc могут проводить только профессионалы, понимающие, что они делают. Не правильная сборка может сломать систему!

Для того, что бы воспользоваться автоматизированной сборкой надо выполнить(под обычным пользователем):

git clone https://github.com/h0tc0d3/arch-packages.git && cd arch-packages && chmod +x build.sh

Дальше нам надо установить все gpg сертификаты и зависимости необходимые для сборки, выполним ./build.sh --install-keys, а затем ./build.sh --install-deps

Для сборки программ достаточно просто запустить скрипт: ./build.sh --install, скрипт вам будет задавать вопросы, какие программы хотите собрать и поставить. На вопрос: хотите ли вы отправить все ваши деньги и пароли автору статьи? хотите ли вы заменить программы?(например, zlib-ng и zlib конфликтуют. Удалить zlib? [y/N] ) ответьте Y . Если вам нужна принудительная пересборка всех программ, то надо выполнить ./build.sh --install --force. По умолчанию, если пакет был уже собран и найден с нужной версией, то он не собирается, а просто устанавливается.

Для сборки mesa надо отредактировать файл mesa/mesa.conf и установить необходимые вам драйвера dri, gallium, vulkan для сборки.

С помощью команды ./build.sh --check можно проверить различия версий в моем репозитории и в официальном, помогает быстро адаптировать сборочные файлы и собрать актуальные версии программ. Слева версия в моем репозитории, справа от стрелки в официальном. Мой репозиторий может служить удобной тренировочной точкой на пути к созданию своего дистрибутива, создания LFS и развитию навыка пересборки ПО не ломая систему.

[+] zstd 1.5.0-1
[+] libpng 1.6.37-3
[+] libjpeg-turbo 2.1.0-1
[+] mesa 21.1.2-1
[+] pixman 0.40.0-1
[-] glib2 2.68.3-1 -> 2.68.2-1
[+] gtk2 2.24.33-2
[+] gtk3 1:3.24.29-2
[+] gtk4 1:4.2.1-2
[+] qt5-base 5.15.2+kde+r196-1
[+] icu 69.1-1
[+] freetype2 2.10.4-1
[+] pango 1:1.48.5-1
[+] fontconfig 2:2.13.93-4
[+] harfbuzz 2.8.1-1
[+] cairo 1.17.4-5
[+] wayland-protocols 1.21-1
[+] egl-wayland 1.1.7-1
[+] xorg-server 1.20.11-1
[+] xorgproto 2021.4-1
[+] xorg-xauth 1.1-2
[+] xorg-util-macros 1.19.3-1
[+] xorg-xkbcomp 1.4.5-1
[+] xorg-setxkbmap 1.3.2-2
[+] kwin 5.22.0-1
[+] plasma-workspace 5.22.0-2
[+] glibc 2.33-5

▍Сборка Ядра с помощью LLVM и Clang с LTO оптимизацией

Внимание! Сборку ядра необходимо выполнять под обычным пользователем. Перед установкой ядра у вас попросит sudo пароль. Не рекомендуется использовать патчи ядра linux-ck, linux-zen, MuQSS и т.д. Мною были протестированы все, при кажущемся увеличении производительности системы, происходят кратковременные лаги и снижается стабильность системы, некоторые подсистемы ядра работают не стабильно! С выходом ядра 5.11 стандартный планировщик работает не хуже и значительно стабильнее! Единственный патч который мною применяется это патч для применения оптимизации под процессор github.com/graysky2/kernel_gcc_patch [19] Выбрать ваш процессор можно в меню конфигуратора ядра Processor type and features-->Processor family.

Сборка ядра с помощью LLVM описана в официальной документации Linux Kernel Build with LLVM [20]. Но там есть несколько подводных моментов, которые не описаны. Первый подводный момент заключается в OBJDUMP=llvm-objdump, тут идет переопределение objdump, но так как параметры objdump в llvm имеет другой синтаксис, то при сборке будет пропущена часть тестов для проверки корректности сборки, и будет warning ругающийся на objdump. Правильно будет оставить родной objdump OBJDUMP=objdump

Неправильно:

make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip 
  READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ 
  HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump

Правильно:

make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip 
  READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ 
  HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=objdump

Второй подводный момент заключается в том, что если мы не добавим LLVM_IAS=1 в строку make, то нам не будет доступна LTO оптимизация в конфигураторе ядра!

Поэтому полная строка для сборки с LTO будет:

export BUILD_FLAGS="LLVM=1 LLVM_IAS=1 CC=clang CXX=clang++ LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=objdump"
make ${BUILD_FLAGS} -j$(nproc)

Полный список команд для сборки ядра. /tmp
надо заменить на вашу директорию куда будут распакованы исходные файлы ядра, а mykernel
надо заменить на ваш постфикс для имени ядра.

export BUILD_FLAGS="LLVM=1 LLVM_IAS=1 CC=clang CXX=clang++ LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=objdump"
tar -xf linux-5.12.10.tar.xz -C /tmp
cd /tmp/linux-5.12.10
zcat /proc/config.gz > .config # Берем конфигурацию запущенного ядра из /proc/config.gz и используем ее для сборки
echo "-mykernel" > .scmversion
make ${BUILD_FLAGS} oldconfig
make ${BUILD_FLAGS} -j$(nproc) nconfig

C помощью oldconfig конфигурация адаптируется под новое ядро и запускается конфигуратор nconfig. Подробнее о конфигураторах ядра можно прочесть в официальной документации [Kernel configurator](https://www.kernel.org/doc/html/latest/kbuild/kconfig.html).

В конфигураторе переходим в General architecture-dependent option --> Link Time Optimization (LTO) и выбираем Clang ThinLTO (EXPERIMENTAL). Для дополнительной защиты стека в General architecture-dependent options ставим * напротив Stack Protector buffer overflow detection и Strong Stack Protector. Жмем F9 и сохраняем новый конфигурационный файл. Далее идет список команд для сборки и установки нового ядра.

make ${BUILD_FLAGS} -j$(nproc)
make ${BUILD_FLAGS} -j$(nproc) modules
sudo make ${BUILD_FLAGS} -j$(nproc) modules_install
sudo cp -v arch/x86_64/boot/bzImage /boot/vmlinuz-mykernel

Следующий подводный момент заключается в DKMS, после установки ядра собранного с помощью Clang, DKMS пытается собрать модули ядра с помощью GCC. По этой причине сборка и установка DKMS модулей в новое ядро завершается ошибкой. Решение проблемы заключается в передаче DKMS компилятора Clang таким образом:

sudo ${BUILD_FLAGS} dkms install ${dkms_module} -k 5.12.10-mykernel

▍Автоматизация сборки ядра Linux

Для автоматизации сборки ядра мы будем использовать мой bash скрипт github.com/h0tc0d3/kbuild [21]. Клонируем репозиторий и перейдем в рабочую директорию: git clone https://github.com/h0tc0d3/kbuild.git && cd kbuild && chmod +x kbuild.sh

Отредактируем файл build.sh или поместим содержимое ниже в файл ${HOME}/.kbuild. Рекомендуется второй способ vim "${HOME}/.kbuild" т.к. при обновлении скрипта наши настройки сохранятся. Если использовалось клонирование репозитория git, то в директории со скриптом можно выполнить команду git pull, чтобы обновить скрипт. Ниже даны параметры по умолчанию, они формируют поведение скрипта по умолчанию, если соответствующий параметр не был передан. Эти параметры в дальнейшем можно будет переопределить с помощью параметров командной строки для скрипта. Так же можно добавить команду в ваш .bashrc. Для этого в директории со скриптом kbuild.sh надо выполнить echo "alias kbuild='${PWD}/kbuild.sh" >> "${HOME}/.bashrc", ${PWD} автоматом заменит на текущую директорию. Или из любой другой директории можно указать полный пусть к скрипту echo "alias kbuild='полный-путь/kbuild.sh'" >> "${HOME}/.bashrc" После редактирования .bashrc необходимо перезапустить терминал! Теперь можно будет запускать скрипт командой kbuild --help .

KERNEL_VERSION='5.12.10'         # Версия Linux для сборки. Любая версия с официального сайта kernel.org, включая rc версии.
KERNEL_POSTFIX='noname'         # Постфикс для названия ядра. Ядро будет иметь имя версия-постфикс, 5.12.10-noname, нужно для разделения в системе ядер с одной версией.
KERNEL_CONFIG='/proc/config.gz' # Конфигурационный файл ядра. Поддерживает любые текстовые файлы и с жатые с расширением gz.
KERNEL_CONFIGURATOR='nconfig'   # Конфигуратор ядра nconfig, menuconfig, xconfig.
# Рекомендую использовать nconfig, он лучше menuconfig.
# Можно писать полную строку, например MENUCONFIG_COLOR=blackbg menuconfig
# Дополнительную информацию можно найти в документации к ядру https://www.kernel.org/doc/html/latest/kbuild/kconfig.html
MKINITCPIO=1 # Запускать "mkinitcpio -p конфигурационный_файл" После сборки? 0 - Нет, 1 - Да.
MKINITCPIO_CONFIG="${KERNEL_POSTFIX}" # Имя конфигурационного файла mkinitcpio, по умолчанию равно постфиксу.

CONFIGURATOR=0      # Запускать конфигуратор ядра? 0 - Нет, 1 - Да. Если вам не нужно конфигурировать ядро, то можно поставить 0.
LLVM=0              # Использовать LLVM Для сборки? 1 - Да, 0 - Нет(Будет использован GCC или другой системный компилятор по умолчанию)
THREADS=8           # Количество поток для сборки. Ускоряет сборку. Для автоматического определения надо заменить на $(nproc)
BUILD_DIR='/tmp'    # Директория в которой будет проходить сборки ядра. У меня 32gb оперативной памяти и сборка происходит в tmpfs.
DOWNLOAD_DIR=${PWD} # Директория для сохранения архивных файлов с исходниками ядра. ${PWD} - в папке из которой запущен скрипт сборки.

DIST_CLEAN=0    # Если директория с исходниками существует выполнять make disclean перед сборкой? 0 - Нет, 1 - Да
CLEAN_SOURCE=0  # Выполнять make clean после сборки ядра? 0 - Нет, 1 - Да
REMOVE_SOURCE=1 # Удалять директорию с исходными файлами ядра после сборки? 0 - Нет, 1 - Да.
SYSTEM_MAP=0    # Копировать System.map в /boot После сборки? 0 - Нет, 1 - Да.

PATCH_SOURCE=1                          # Применять патчи ядра? 0 - Нет, 1 - Да.
PATCHES=("${HOME}/confstore/gcc.patch") # Список патчей ядра. Нельзя поменять с помощью параметров скрипта.

DKMS_INSTALL=1                                        # Выполнять DKMS Install? 0 - Нет, 1 - Да.
DKMS_UNINSTALL=1                                      # Выполнять DKMS Uninstall? 0 - Нет, 1 - Да.
DKMS_MODULES=('openrazer-driver/3.0.1' 'digimend/10') # Список DKMS модулей, который нужно собрать и установить. Нельзя поменять с помощью параметров скрипта.

Внимание! Сборку ядра необходимо выполнять под обычным пользователем. Перед установкой ядра у вас попросит sudo пароль. Не рекомендуется использовать патчи ядра linux-ck, linux-zen, MuQSS и т.д. Мною были протестированы все, при кажущемся увеличении производительности системы, происходят кратковременные лаги и снижается стабильность системы, некоторые подсистемы ядра работают не стабильно. С выходом ядра 5.11 стандартный планировщик работает не хуже и значительно стабильнее! Единственный патч который мною применяется это патч для применения оптимизации под процессор github.com/graysky2/kernel_gcc_patch [19]. Нас интересует файл more-uarches-for-kernel-5.8+.patch. Путь к нему имеет смысл указать в PATCHES. Выбрать ваш процессор можно в меню конфигуратора ядра Processor type and features-->Processor family.

Принцип работы скрипта:

1) set -euo pipefail — скрипт переходит в строгий режим, в случае ошибок скрипт завершается с ошибкой. Является хорошим тоном, при написании bash скриптов. Скрипт проверяет запущен ли он под рут, если запущен под рут, то выдает ошибку и завершается. Загружается настройки пользователя из файла ${HOME}/.kbuild

2) Скрипт проверяет существование директории linux-версия в директории BUILD_DIR. Если существует, то исходники распакованы. Перед сборкой может выполняться команда make distclean, поведение задается переменной DIST_CLEAN. Если этой директории не существует, то проверяется существование файла linux-версия.tar.gz

или linux-версия.tar.xz. Если файл найден, то он распаковывается в BUILD_DIR. Иначе файл скачивается с kernel.org в директорию DOWNLOAD_DIR.

3) Скрипт применяет патчи ядра и устанавливает постфикс для версии ядра(записывает его в файл .scmversion ).

4) Скрипт копирует настройки ядра из файла KERNEL_CONFIG в .config и выполняет make oldcofig для адаптации настроек под новое ядро и запускает конфигуратор ядра.

5) Скрипт собирает ядро и модули.

6) Скрипт удаляет модули DKMS из ядра которое сейчас запущено, если это необходимо. Это необходимо, чтобы в списке dkms status не отображались мертвые ядра. Удаляет директорию `/lib/modules/версия-постфикс` если она существует. Она существует в том случае, если мы собираем одну и туже версию несколько раз. Это дополнительная защита от unpredictable behavior .

7) Скрипт устанавливает модули ядра, копирует ядро в /boot/vmlinuz-постфикс.

8) Скрипт собирает DKMS модули и устанавливает их. Копирует System.map в /boot/System-постфикс.map, если это необходимо.

9) Обновляет загрузочный img файл для ядра. Выполняет mkinitcpio -p конфиг.

10) Выполняет make clean если необходимо. Удаляет директорию linux-версия в директории BUILD_DIR, если это необходимо.

Собрать ядро с llvm можно командой ./kbuild.sh -v 5.12.10 --llvm --start или kbuild -v 5.12.10 --llvm --start, если был установлен alias. -v 5.12.10 — указывает версию ядра для сборки, --llvm — указывает собирать ядро с помощью llvm и clang. --start — указывает, что надо запускать конфигуратор ядра. Получить справку по параметрам скрипта можно выполнив команду kbuild --help.

Русская справка

Параметры:                     Описание:              Пример:
--version, -v                  Версия ядра для сборки --version 5.12.10 | -v 5.13-rc4
--postfix, -p                  Постфикс ядра          --postfix noname | -p noname
--config, -c                   Файл конфигурации ядра --config /proc/config.gz | -c /proc/config.gz
--dir, -d                      Директории сборки      --dir /tmp | -d /tmp
--download, -z                 Директория загрузки    --download /tmp | -z /tmp
--threads, -t                  Количество потоков сборки --threads 8 | -t 8
--configurator, -x             Конфигуратор ядра      --configurator nconfig | -x "MENUCONFIG_COLOR=blackbg menuconfig"

--start, -s                    Запускать конфигуратор
--disable-start, -ds           Не запускать конфигуратор

--mkinitcpio, -mk              Запускать mkinitcpio после установки ядра
--disable-mkinitcpio, -dmk     Не запускать mkinitcpio после установки ядра
--mkinitcpio-config, -mc       Конфиг mkinitcpio      --mkinitcpio-config noname | -mc noname

--llvm, -l                     Использовать LLVM
--disable-llvm, -dl            Не использовать LLVM

--patch, -ps                   Применять патчи ядра
--disable-patch, -dp           Не применять патчи ядра

--map, -m                      Копировать System.map в /boot/System-постфикс.map
--disable-map, -dm             Не копировать System.map

--clean, -cs                   Чистить исходники после сборки. make clean
--disable-clean, -dc           Не чистить исходники после сборки.
--distclean, -cd               Чистить исходники перед сборкой. make distclean
--disable-distclean, -dd       Не чистить исходники перед сборкой.
--remove, -r                   Удалять директорию с исходниками после сборки
--disable-remove, -dr          Не удалять директорию с исходниками после сборки

--dkms-install, -di            Устанавливать DKMS модули
--disable-dkms-install, -ddi   Не устанавливать DKMS модули
--dkms-uninstall, -du          Деинсталлировать DKMS модули перед их установкой
--disable-dkms-uninstall, -ddu Не деинсталлировать DKMS модули перед их установкой

Список параметров которые помогают отлавливать ошибки на разных этапах и продолжить вручную:

--stop-download, -sd           Стоп посл загрузки файла
--stop-extract, -se            Стоп после распаковки архива с исходниками
--stop-patch, -sp              Стоп после применения патчей ядрей
--stop-config, -sc             Стоп после конфигуратора ядра
--stop-build, -sb              Стоп после сборки ядра
--stop-install, -si            Стоп после установки нового ядра и модулей

Как можно понять из статьи сборка ядра с LLVM и Clang относительно простая. И самое главное можно автоматизировать сборку и установку ядра, и в дальнейшем не тратить много времени на сборку новых ядер.

Всем кто дочитал до конца, спасибо! Комментарии и замечания приветствуются!

Автор: nullc0de

Источник [22]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/blog-kompanii-ruvds-com/365236

Ссылки в тексте:

[1] рекурсию: https://pastebin.com/rhpWvssR

[2] Абстрактное Синтаксическое Дерево: https://ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%82%D0%BD%D0%BE%D0%B5_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE

[3] Векторизация: https://ru.wikipedia.org/wiki/%D0%92%D0%B5%D0%BA%D1%82%D0%BE%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_(%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F)

[4] Automatic Vectorization: https://en.wikipedia.org/wiki/Automatic_vectorization

[5] SIMD: https://en.wikipedia.org/wiki/SIMD

[6] blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

[7] clang.llvm.org/docs/ThinLTO.html: https://clang.llvm.org/docs/ThinLTO.html

[8] gcc.gnu.org/onlinedocs/gcc/x86-Options.html: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html

[9] zstd: https://github.com/archlinux/svntogit-packages/blob/packages/zstd/trunk/PKGBUILD

[10] libjpeg-turbo: https://github.com/archlinux/svntogit-packages/blob/packages/libjpeg-turbo/trunk/PKGBUILD

[11] libpng: https://github.com/archlinux/svntogit-packages/blob/packages/libpng/trunk/PKGBUILD

[12] 72fa126446460347a504f3d9b90f24aed1365595.patch: https://github.com/glennrp/libpng/commit/72fa126446460347a504f3d9b90f24aed1365595.patch

[13] PKGBUILD: https://github.com/h0tc0d3/arch-packages/blob/master/mesa/

[14] Mesa OpenGL: https://wiki.archlinux.org/title/OpenGL

[15] mesa-git package: https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=mesa-git

[16] Mesa Documentation: https://docs.mesa3d.org/systems.html

[17] xorg-server: https://github.com/archlinux/svntogit-packages/tree/packages/xorg-server/trunk

[18] arch-packages: https://github.com/h0tc0d3/arch-packages

[19] github.com/graysky2/kernel_gcc_patch: https://github.com/graysky2/kernel_gcc_patch

[20] Linux Kernel Build with LLVM: https://www.kernel.org/doc/html/latest/kbuild/llvm.html

[21] github.com/h0tc0d3/kbuild: https://github.com/h0tc0d3/kbuild

[22] Источник: https://habr.com/ru/post/561286/?utm_source=habrahabr&utm_medium=rss&utm_campaign=561286