Технический прогресс не стоит на месте, появляются новые
компьютерные архитектуры, компиляторы становятся умнее и генерируют
более быстрый машинный код. Современные задачи требуют все более
креативного и эффективного решения. В данной статье пойдет речь, на
мой взгляд, про один из самых прогрессивных тулчейнов LLVM и
компиляторы на его основе Clang и Clang++, для языков
программирования С и C++ соответственно. Хоть GCC конкурент Clang,
может агрессивнее оптимизировать циклы и
рекурсию,
Clang дает на выходе более корректный машинный код, и чаще всего не
ломает поведение приложений. Плюс оптимизация программ не
заканчивается только оптимизацией циклов, поэтому Clang местами
дает лучшую производительность. В GCC же за счет переоптимизации
вероятность получить
unpredictable
behavior значительно выше. По этой причине на многих
ресурсах не рекомендуют использовать
-O3 и
LTO(Link Time
Optimization) оптимизации для сборки программ. Плюс в
случае агрессивной оптимизации, размер исполняемых файлов может
сильно увеличиться и программы на практике будут работать даже
медленнее. Поэтому мы остановились на Clang не просто так и опции
компиляции -O3 и LTO работают в нем более корректно. Плюс
современные компиляторы более зрелые, и сейчас уже нет тех детских
болячек переоптимизации и LTO.
Что меня побудило написать эту статью? В
первую очередь это несколько фактов:
- Впервые прочел про сборку ядра Linux с LTO оптимизацией и Clang
из новостей, где упоминалась компания Google. Она использует Clang и LTO оптимизацию
для сборки ядра Linux и получения лучшей производительности.
Компания Google для меня является синонимом инноваций, лучших
программистов в мире и поэтому для меня ее опыт является самым
авторитетным. Плюс она привнесла очень много в развитие open
source, и ее наработками пользуются тысячи компаний во всем
мире.
- Хоть компания Google начала использовать Clang и LTO
оптимизацию раньше, только с выходом ядра Linux 5.12.6 и 5.12.7
было закрыто большое количество багов, и сборка ядра c LTO
оптимизаций стала доступна многим. До этого при сборке ядра с LTO
оптимизацией многие драйвера давали сбой.
- Мною уже протестирована работа ядра с 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
и т.д.
- Данная возможность толком нигде не была описана в связи с п.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+5 можно
заменить на 7, чтобы при работе приложения не вычислять его
значение каждый раз и тем самым сделать его быстрее и т.д.
- Оптимизированное дерево может быть преобразовано в машинный
псевдокод понятный компилятору. Псевдокод используется для
дополнительной оптимизации, упрощает разработку универсального
компилятора для разных архитектур процессора, например для x86-64 и
ARMv7\. Так же как ASM листинг, этот псевдокод еще используется,
чтобы понять, как компилятор генерирует машинный код, и служит для
понимания работы компилятора, поиска ошибок, например, ошибок
оптимизации и unpredictable
behavior. Стоит заметить этот этап не является
обязательным и в некоторых компиляторах отсутствует.
- Происходит векторизация.
Векторизация ,Automatic
Vectorization, SIMD
- Генерируется объектный файл. Объектный файл содержит в себе
машинный код для компьютера, и специальные служебные структуры, в
которых все еще есть неизвестные адреса функций и данных, поэтому
этот файл все еще не может быть запущен на исполнение. Чтобы
разрешить неизвестные адреса, был добавлен этап линковки.
Компьютер работает только с бинарными данными, и может оперировать
только адресам, поэтому имя функции и переменной ему ничего не
говорят. Имя это лишь формальность для программистов, чтобы удобнее
было читать исходный код. Во многих компилируемых языках
программирования невозможно вычислить адрес функций на этапе
компиляции, поэтому в них был придуман механизм описания
функций.
Если мы используем функцию и она реализована в каком-то другом
файле, то мы должны описать ее имя, параметры и возвращаемое
значение. Тем самым мы скажем компилятору, что не надо искать ее в
этом файле и она будет добавлена на этапе линковки. Так же, это
упрощает парсинг исходных файлов. Нам больше не надо для компиляции
и разрешения адресов читать все файлы за один раз. Представьте
себе, что у вас исходный код программы занимает несколько гигабайт,
такой размер нередко встречается в серьезных программах, тогда
оперативной памяти большинства компьютеров не хватило бы, чтобы
хранить все служебные структуры компилятора, что значительно
повысило бы стоимость разработки и самих программ.
На этапе линковки:
- Происходит подстановка адресов
- Добавляются дополнительных данных для работы программы,
например ресурсы
- Происходит сборка всех объектных файлов в конечный исполняемый
файл или распространяемую библиотеку, которая может быть
использована в других программах
Понимая это мы можем понять, что
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](http://personeltest.ru/aways/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](http://personeltest.ru/aways/aur.archlinux.org/packages/llvm11-libs-bin/)
с необходимыми библиотеками для обратной совместимости, который
позволяет оставить зависимые программы рабочими. Но данный пакет
работает только с моим сборочным пакетом
[llvm12-git](http://personeltest.ru/aways/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=clangexport CXX=clang++export LD=ld.lldexport CC_LD=lldexport CXX_LD=lldexport AR=llvm-arexport NM=llvm-nmexport STRIP=llvm-stripexport OBJCOPY=llvm-objcopyexport OBJDUMP=llvm-objdumpexport READELF=llvm-readelfexport RANLIB=llvm-ranlibexport HOSTCC=clangexport HOSTCXX=clang++export HOSTAR=llvm-arexport 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.
Изменим количество потоков в 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 Flagsexport CC=clangexport CXX=clang++export LD=ld.lldexport CC_LD=lldexport CXX_LD=lldexport AR=llvm-arexport NM=llvm-nmexport STRIP=llvm-stripexport OBJCOPY=llvm-objcopyexport OBJDUMP=llvm-objdumpexport READELF=llvm-readelfexport RANLIB=llvm-ranlibexport HOSTCC=clangexport HOSTCXX=clang++export HOSTAR=llvm-arexport HOSTLD=ld.lldCPPFLAGS="-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 systemsMAKEFLAGS="-j20"NINJAFLAGS="-j20"#-- Debugging flagsDEBUG_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=clangexport CXX=clang++export LD=ld.lldexport CC_LD=lldexport CXX_LD=lldexport AR=llvm-arexport NM=llvm-nmexport STRIP=llvm-stripexport OBJCOPY=llvm-objcopyexport OBJDUMP=llvm-objdumpexport READELF=llvm-readelfexport RANLIB=llvm-ranlibexport HOSTCC=clangexport HOSTCXX=clang++export HOSTAR=llvm-arexport HOSTLD=ld.lldexport 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, создать директорию, например zstd, а в ней создать файл
PKGBUILD и в него вставить содержимое по ссылке. Дальше в консоли
перейти в директорию содержащую PKGBUILD, выполнить команду
makepkg -cfi
.
libjpeg-turbo Библиотека для
работы c jpeg файлами. Ее очень часто используют браузеры и
программы рабочего стола. libjpeg-turbo собранный с clang дает у
меня лучшую производительность. Действия такие же, как в zstd.
Создать директорию, и вставить в файл PKGBUILD содержимое по ссылке
libjpeg-turbo. Дальше в консоли перейдите в директорию
содержащую PKGBUILD, выполнить команду
makepkg
-cfi
.
libpng Библиотека для работы с
PNG файлами. По сборке и установке все то же самое.
libpng. Для сборки вам понадобится патч:
72fa126446460347a504f3d9b90f24aed1365595.patch, его надо
положить в одну директорию с файлом 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 оптимизированный для сборки с помощью Clang.
Для сборки вам надо отредактировать файл
mesa.conf
и
установить необходимые вам драйвера dri, gallium, vulkan для
сборки. У меня сборка только под новые видеокарты AMD. Подглядеть
можно тут:
Mesa
OpenGL,
mesa-git package,
Mesa
Documentation. При выходе новой версии Mesa не забудьте сменить
21.1.2 на новую версию. А после смены версии обновите контрольные
суммы файлов, выполнив в директории с PKGBUILD команду
updpkgsums
.
xorg-server X сервер с которым
взаимодействуют почти все среды рабочего стола. Сборка дает
заметное улучшение отклика рабочего стола. Сборка такая же через
mapkepkg -cfi
. Скачать необходимые файлы для сборки
можно тут:
xorg-server Сборочный пакет немного кривой и собирает пакет без
оптимизаций. Поэтому его надо пропатчить. Для это после строки
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.
Список был создан с помощью системного профилировщика
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 Выбрать ваш процессор
можно в меню конфигуратора ядра Processor
type and features-->Processor family.
Сборка ядра с помощью LLVM описана в официальной документации
Linux Kernel Build with LLVM. Но там есть несколько подводных
моментов, которые не описаны. Первый подводный момент заключается в
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 /tmpcd /tmp/linux-5.12.10zcat /proc/config.gz > .config # Берем конфигурацию запущенного ядра из /proc/config.gz и используем ее для сборкиecho "-mykernel" > .scmversionmake ${BUILD_FLAGS} oldconfigmake ${BUILD_FLAGS} -j$(nproc) nconfig
C помощью
oldconfig конфигурация адаптируется под новое ядро
и запускается конфигуратор nconfig. Подробнее о конфигураторах ядра
можно прочесть в официальной документации [Kernel
configurator](http://personeltest.ru/aways/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) modulessudo make ${BUILD_FLAGS} -j$(nproc) modules_installsudo 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.
Клонируем репозиторий и перейдем в рабочую директорию:
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.htmlMKINITCPIO=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. Нас интересует файл
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 относительно
простая. И самое главное можно автоматизировать сборку и установку
ядра, и в дальнейшем не тратить много времени на сборку новых
ядер.
Всем кто дочитал до конца, спасибо! Комментарии и замечания
приветствуются!