4. Экстра оптимизации#

4.1. Масштабирование частоты CPU#

В Linux существует специальные модули ядра, так называемые драйверы масштабирования CPU. Они корректируют частоту вашего процессора в режиме реального времени в зависимости от используемой политики (governor) масштабирования частот процессора. Для x86 архитектуры стандартным таким драйвером в ядре Linux принято считать acpi-cpufreq, который, как понятно из названия, осуществляет регулировку путем специальных вызовов APCI, которые устанавливают так называемый P-state (Peformance state) уровень для вашего процессора. Уровни P-State вашего процессора можно сравнить с коробкой передач у автомобиля: он может лететь на всей скорости, ехать с обычной автомобиля: он может лететь на всей скорости, ехать с обычной скоростью, и стоять на месте в ожидании того, когда надо будет сдвинуться с места. Собственно, по этой аналогии acpi-cpufreq и переключается между 3-х стандартных P-State уровней в зависимости от используемой политики масштабирования.

4.1.1. Политики масштабирования частоты#

Вернемся к политикам масштабирования частоты. Их можно сравнить с планами электропитания в настройках Windows, только в отличии от них политик масштабирования в Linux довольно много: performance, powersave, userspace, ondemand, conservative, schedutil.

Рассмотрим каждую из них подробнее:

performance - как понятно из названия, данная политика используется для достижения максимальной производительности, так как она сильно снижает порог нагрузки, переходя через который процессор начинает работать на полную мощность. Хорошо подходит для настольных ПК, но не слишком желательно для использования на ноутбуках, где важна автономность. Обратите внимание, что поведение данной политики зависит от используемого драйвера масштабирования, об этом подробнее читайте далее в разделе Альтернативные драйверы масштабирования.

powersave - полная противоположность performance, минимизирует потребление энергии через занижение частот процессора до минимума. Может быть очень полезно для ноутбуков при работе от батареи. Обратите внимание, что поведение данной политики зависит от используемого драйвера масштабирования, об этом подробнее читайте далее в разделе Альтернативные драйверы масштабирования.

ondemand - политика, которая регулирует частоту процессора на основе общей нагрузки на процессор, то есть частота прямо пропорциональна нагрузке: Чем выше нагрузка, тем больше частота, и наоборот, чем ниже, тем ниже и частота. Конечно, скорость возрастания частоты и нагрузки не всегда коррелируют между собой, ибо для принятия решения о том, когда нужно повышать частоту до максимальных значений, драйвер руководствуется значением параметра up_threshold (по умолчанию 80%), которое устанавливает порог максимальной нагрузки в процентах, которое должно достичь хотя бы одно из ядер вашего процессора. К примеру, если у вас есть два ядра, и в текущий момент времени драйвер масштабирования зафиксировал на одном ядре процессора нагрузку в 70%, а на другом в 81%, то он станет повышать их частоты в соответствии со значением up_threshold по умолчанию. Обратное решение - понижение частоты CPU, драйвер принимает в соответствии со значением другого параметра - down_threshold, которое также устанавливает порог ниже которого должно пройти хотя бы одно ядро процессора, тогда частота будет понижена. Во всех остальных случаях частота будет регулироваться как обычно, то есть пропорционально общей нагрузке.

Довольно хороший выбор для большинства конфигураций и задач. Рекомендуется к использованию.

conservative - должно быть вы замечали, как запуская игру или "тяжёлое" приложение ваш компьютер или ноутбук начинает гудеть как самолет, так как происходит резкое повышение частот процессора и следовательно растет его температура. conservative очень похож на политику ondemand, но он делает процесс увеличения частоты CPU более "гладким" и поступательным даже при значительном повышении нагрузки. Это может быть очень полезным, когда вы не хотите чтобы ваш ноутбук резко повышал свою температуру или уходил в так называемый "турбобуст".

schedutil - регулирует частоту процессора на основе метрик планировщика CPU, например таких как количество активных процессов на ядро процессора. Не слишком рекомендуется в силу того, что использование данной политики часто приводит к резким скачкам частоты без необходимости. Несмотря на это, данная политика используется по умолчанию в стандартном ядре Arch Linux.

userspace - данная политика является заглушкой, которая позволяет передать полномочия управления частотой с драйвера масштабирования на программу, запущенную с правами root, в пространстве пользователя, которая и будет осуществлять процесс регулировки частоты в соответствии с собственной логикой. В большинстве случаев вам никогда не понадобиться эта политика.

4.1.2. Альтернативные драйверы масштабирования#

Современные процессоры Intel и AMD могут самостоятельно осуществлять масштабирование своей частоты на основе информации получаемой через механизм CPPC (Collaborative Processor Performance Control), с которым процессор получает от драйвера масштабирования "подсказку", о том какой уровень производительности следует выдавать в данный момент. Специально для работы с этим механизмом были созданы драйверы amd-pstate (поддерживается процессорами Zen 2 и выше) и intel-pstate (поддерживается Sandy Bridge и выше) для процессоров AMD и Intel соответственно.

Примечание

Для процессоров Intel термин CPPC обычно не используется, так как их технология для автономного управления частотой процессора называется HWP (Hardware P-states), но суть остается той же, что и в случае с CPPC.

У драйверов P-state существует несколько режимов работы. Как для intel-pstate, так и для amd-pstate есть active и passive режим, но для amd-pstate есть также guided режим.

В режиме active, который используется по умолчанию во всех P-state драйверах, управление частотой выполняется полностью автономно самим процессором, но он получает от драйвера масштабирования "подсказку" - так называемый уровень energy_performance_preference (далее EPP), на основе которого процессор понимает с каким уклоном ему регулировать собственную частоту. Таких уровней всего пять: power, balance_power, default, balance_performance, performance. Как понятно из названия, эти уровни указывают процессору предпочтение к тому, чтобы он работал на максимальную мощность (при использовании уровней balance_performance и performance) или наоборот экономил энергию и чаще принимал решение о понижении своей частоты или уходе в состояние сна. По умолчанию используется default, что представляет собой баланс между максимальной производительностью и энергосбережением.

Важно отметить, что классические политики для управления частоты, которые мы описывали ранее, отходят на второй план, и более того, в режиме active вы сможете выбрать всего две "фиктивные" политики масштабирования, это powersave и performance. Обе из них не оказывают того влияния на частоту процессора, которое мы приписывали им ранее, так как в режиме active драйвер не может самостоятельно устанавливать частоту процессора и теперь это зависит только от используемого значения EPP. Поэтому при выборе performance политики вы на самом деле просто измените текущий уровень EPP на performance, значение которого P-state драйвер передаст процессору через специальный регистр. Но при переключении политики на powersave уровень EPP не измениться и вы должны будете установить его самостоятельно (об этом читайте далее).

При использовании режима passive P-State драйвер может напрямую устанавливать желаемый уровень производительности, в связи с чем в нем доступен полный набор политик масштабирования, о которых мы говорили ранее. При этом установить уровень EPP становится невозможно, так как процессор уже не управляет частотой полностью самостоятельно, а ожидает переключения уровня P-State со стороны драйвера масштабирования. Данный режим отличается от использования классического драйвера acpi-cpufreq тем, что драйвер переключается не между 3-мя уровнями P-State, которые определены стандартом ACPI, а между сразу всеми доступными диапазонами частоты для вашего процессора, что гораздо эффективнее.

Для драйвера amd-pstate существует также третий режим работы - guided. Он работает аналогично режиму active, позволяя процессору самому управлять частотой, но при этом драйвер может устанавливать процессору пороги минимальной и максимальной частоты, что позволяет использовать классические политики масштабирования как в случае с passive режимом.

Переключение между всеми тремя режимами может быть осуществлено как при помощи соответствующих параметров ядра amd_pstate (например, amd_pstate=guided) или intel_pstate в зависимости от используемого драйвера масштабирования, так и прямо во время работы системы при помощи файла status на псевдофайловой системе sysfs:

echo "passive" | sudo tee /sys/devices/system/cpu/amd_pstate/status
echo "passive" | sudo tee /sys/devices/system/cpu/intel_pstate/status

4.1.3. Настройка параметров масштабирования#

Перейдем от теории к практике. Чтобы изменить текущую политику масштабирования частоты можно воспользоваться множеством различных способов, начиная от способа "руками" при помощи sysfs, заканчивая специализированными утилитами как cpupower и power-profiles-daemon, которые мы и будем использовать для удобства. Для начала установим программу cpupower:

sudo pacman -S cpupower

С её помощью мы можем быстро увидеть информацию о текущей политике масштабирования, используемом драйвере, а также текущую частоту:

cpupower frequency-info

Установить желаемую политику масштабирования можно через команду frequency-set. К примеру, установим политику performance:

sudo cpupower frequency-set -g performance

Примечание

Если команда cpupower frequency-info указывает на то, что используется P-State драйвер в автономном режиме, то не следует пытаться применять классические политики масштабирования при помощи cpupower, вместо этого нужно указывать значение параметра energy_performance_preference (EPP) при помощи sysfs, например:

echo "balance_performance" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference

Узнать доступные значения параметра EPP можно через:

cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences

Это значение будет той самой подсказкой для процессора, о которой мы говорили выше, в соответствии с которой он будет осуществлять самостоятельный контроль своей частоты.

Если вы хотите ограничить максимальную частоту процессора, то вы можете использовать ключ -u:

# Ограничит максимальную частоту в 3 ГГц
sudo cpupower frequency-set -u 3.0Ghz

Все проделанные изменения выше работают только до перезагрузки системы, чтобы их сохранить нам понадобиться одноименный демон cpupower.service:

sudo systemctl enable --now cpupower

А также изменить конфигурацию /etc/default/cpupower, которая содержит все применяемые при запуске системы настройки. К примеру, если вам нужно изменить политику масштабирования на постоянной основе, то нужно указать значение параметра governor внутри /etc/default/cpupower:

sudo nano /etc/default/cpupower#
governor='conservative'

Примечание

По умолчанию все строки в файле закоментированы. Чтобы раскоментировать нужные параметры уберите знак # в начале строки.

4.2. Отключение спящего режима и гибернации#

У некоторых моделей материнских плат (в особенности китайских) может некорректно или полностью не работать выход из состояния сна или гибернации. В этом случае может быть полезным полностью отключить переход в них при помощи polkit - графического менеджера по передаче привелегий root пользователя, с помощью которого также можно ограничивать доступ к определенным ресурсам. Установить его можно из официальных репозиториев:

sudo pacman -S polkit

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

sudo nano /etc/polkit-1/rules.d/10-disable-suspend.rules#
polkit.addRule(function(action, subject) {
  if (action.id == "org.freedesktop.login1.suspend" ||
      action.id == "org.freedesktop.login1.suspend-multiple-sessions" ||
      action.id == "org.freedesktop.login1.hibernate" ||
      action.id == "org.freedesktop.login1.hibernate-multiple-sessions")
  {
      return polkit.Result.NO;
  }
});

4.3. Повышение лимитов#

Отредактируйте файлы:

sudo nano /etc/systemd/system.conf
sudo nano /etc/systemd/user.conf

Изменените значения DefaultLimitNOFILE= на 523288 (можно удвоить).

systemctl daemon-reload

Для Arch Linux необходимо так же прописать лимиты в /etc/security/limits.conf (в самый нижний столбец):

username hard nofile 524288

Вместо username нужно вписать ваше имя пользователя.

../_images/limits.jpg

Эти изменения необходимы для правильной работы технологи Esync и увеличения плавности системы, так как параметры по умолчанию могут быть слишком малы. Подробнее здесь.

4.4. Отключение файла подкачки#

Для лучшей игровой производительности следует использовать файловую систему Btrfs и не задействовать файл подкачки (вместо него стоит использовать выше упомянутый zramswap, конечно при условии что у вас не слишком слабый процессор и оперативной памяти больше чем 4 ГБ), а также без страха отключать фиксы уязвимостей, которые сильно урезают производительность процессора (о них написано в следующем разделе).

sudo swapoff /dev/sdxy  # Вместо xy ваше название (Например sdb1).
sudo swapoff -a         # Отключает все swap-разделы/файлы
sudo rm -f /swapfile    # Удалить файл подкачки с диска
sudo nano /etc/fstab    # Уберите самую нижнюю строчку полностью.

4.5. Форсирование использования Clang при сборке пакетов#

В системах на базе ядра Linux различают две основных группы компиляторов, это LLVM и GCC. И те, и другие хорошо справляются с возложенными на них задачами, но LLVM имеет чуть большее преимущество с точки зрения производительности при меньших потерях в качестве конечного кода. Поэтому в целом применение компиляторов LLVM для сборки различных пакетов при задании флага -O3 (максимальная производительность) является совершенно оправданным, и может дать реальный прирост при работе программ.

Компилятором для языков C/C++ в составе LLVM является Clang и Clang++ соответственно. Его использование при сборке пакетов мы и будем форсировать через makepkg.conf

Для начала выполним их установку:

sudo pacman -Syu llvm clang lld mold openmp

Теперь клонируем уже готовый конфигурационный файл /etc/makepkg.conf под новыми именем в домашнюю директорию ~/.makepkg-clang.conf:

cp /etc/makepkg.conf ~/.makepkg-clang.conf

Это поможет нам в случае чего откатиться к использованию компиляторов GCC если возникнут проблемы со сборкой пакетов через LLVM/Clang.

Теперь откроем выше скопированный файл и добавим туда после строки CHOST="x86_64-pc-linux-gnu" следующее:

export CC=clang
export CXX=clang++
export LD=ld.lld
export CC_LD=mold
export CXX_LD=mold
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 CXXFLAGS="${CFLAGS}"
export LLVM=1
export LLVM_IAS=1
export CCLDFLAGS="$LDFLAGS"
export CXXLDFLAGS="$LDFLAGS"

При использовании Clang из пакета llvm-git (установка описана ниже) стоит включить использование LLVM OpenMP и компоновщика mold, а также другие флаги при сборке пакетов:

CFLAGS="-march=native -mtune=native -O3 -fexceptions -fopenmp \
        -falign-functions=32 -fno-math-errno -fno-trapping-math \
        -fcf-protection=none -mharden-sls=none -Wp,-D_FORTIFY_SOURCE=2 \
        -Wformat -Werror=format-security -fstack-clash-protection"
CXXFLAGS="$CFLAGS -Wp,-D_GLIBCXX_ASSERTIONS"
export CFLAGS_KERNEL="$CFLAGS"
export CXXFLAGS_KERNEL="$CXXFLAGS"
export CFLAGS_MODULE="$CFLAGS"
export CXXFLAGS_MODULE="$CXXFLAGS"
export KBUILD_CFLAGS="$CFLAGS"
export KCFLAGS="-O3"
export KCPPFLAGS="$KCFLAGS"
LDFLAGS="-Wl,-O3,--sort-common,--as-needed,-lgomp,-z,pack-relative-relocs,-z,relro,-z,now"
LTOFLAGS="-flto=auto"
RUSTFLAGS="-C opt-level=3 -C target-cpu=native -C link-arg=-z -C link-arg=pack-relative-relocs"
#-- Make Flags: change this for DistCC/SMP systems
MAKEFLAGS="-j$(nproc)"
NINJAFLAGS="-j$(nproc)"
OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge !debug lto)

Предупреждение

Здесь мы используем некоторые флаги которые не рекомендуется использовать с точки зрения безопасности конечного кода для того чтобы увеличить производительность, как например -fcf-protection=none и -mharden-sls=none, но если для вас безопасность такой же важный аспект как и производительность, то замените их на соответствующие флаги на -fstack-clash-protection и -fcf-protection (флаг -mharden-sls можно просто опустить).

Отлично, теперь вы можете собрать нужные вам пакеты (программы) через LLVM/Clang просто добавив к уже известной команде makepkg следующие параметры:

makepkg --config ~/.makepkg-clang.conf -sric --skippgpcheck --skipchecksums

Внимание

Далеко не все пакеты так уж гладко собираются через Clang, в частности не пытайтесь собирать им Wine/DXVK, т.к. это официально не поддерживается и с 98% вероятностью приведет к ошибке сборки. Но в случае неудачи вы всегда можете использовать компиляторы GCC, которые у вас заданы в настройках makepkg.conf по умолчанию, т.е. просто уберите опцию --config ~/.makepkg-clang.conf из команды makepkg.

Мы рекомендуем вам пересобрать наиболее важные пакеты. Например такие как драйвера (то есть mesa, lib32-mesa, если у вас Intel/AMD), Xorg сервер, а также связанные с ним компоненты, или Wayland, критически важные пакеты вашего DE/WM, например: gnome-shell, plasma-desktop. А также композиторы kwin, mutter, picom и т.д. в зависимости от того, чем именно вы пользуетесь.

Больше подробностей по теме вы можете найти в данной статье:

https://habr.com/ru/company/ruvds/blog/561286/

4.5.1. Ускорение работы компиляторов LLVM/Clang#

Дополнительно можно отметить, что после установки Clang вы можете перекомпилировать его самого через себя, т.е. выполнить пересборку Clang с помощью бинарного Clang из репозиториев. Это позволит оптимизировать уже сам компилятор под ваше железо и тем самым ускорить его работу при сборке уже других программ. Аналогичную операцию вы можете проделать и с GCC.

Делается это через пакет в AUR:

# Сборка LLVM
git clone https://aur.archlinux.org/llvm-git
cd llvm-git
makepkg -sric --config ~/.makepkg-clang.conf

4.6. Установка оптимизированных пакетов#

Нативная компиляция - это конечно хорошо, но не у каждого человека есть время заниматься подобными вещами, да и всю систему пересобирать с нативными флагами тоже никто не будет (иначе вам сюда: https://gentoo.org). Возникает вопрос: как сделать все с наименьшим количеством напряга?

Для начала сделаем небольшое отступление. У архитектуры x86_64 различают несколько поколений или "уровней". Это x86_64, x86_64_v2, x86_64_v3 и x86_64_v4 (новейшие процессоры). Различия между этими "поколениями" состоят в применяемом наборе инструкций и возможностей процессора. Например, если вы собираете программу для x86_64_v2, то вы автоматически задействуете инструкции SSE3, SSE4_1 и т.д. При этом такая программа не будет работать на предыдущих поколениях, то есть на процессорах которые не поддерживают набор инструкций x86_64_v2. При этом к x86_64_v2 и другим уровням относятся различные процессоры, как AMD, так и Intel. При этом логично, что чем выше поколение x86_64 поддерживает ваш процессор, тем больше будет производительность за счет использования многих оптимизаций и доп. инструкций. Подробнее об этих уровнях или же поколениях можете прочитать здесь (англ.).

Смысл в том, что существует сторонний репозиторий Arch Linux - ALHP, который содержит все пакеты из официальных репозиториев, но собранных для процессоров x86_64_v2 или x86_64_v3. То есть это те же самые, уже готовые пакеты из официальных репозиториев, но собранные с различными оптимизациями для определенной группы процессоров (поколений x86_64).

Опасно

Прежде чем мы подключим данный репозиторий, нужно обязательно понять к какому поколению относиться ваш процессор, иначе, если вы установите пакеты собранные для x86_64_v3, но ваш процессор при этом не будет относиться к поколению x86_64_v3, то ваша система станет полностью не работоспособной, хотя её и все ещё можно будет восстановить через LiveCD окружение при помощи pacstrap.

Опасно

Оптимизированные пакеты для процессоров Intel поддерживают только полные процессоры серий Core 2 и i3/i5/i7. Многие процессоры Pentium/Celeron не имеют полного набора инструкций, необходимого для использования оптимизированных пакетов. Пользователям этих процессоров следует установить универсальные пакеты или пакеты оптимизированные ниже на один уровень (то есть если у вас поддерживается v3, то подключайте репозиторий с v2 и т.д.), даже если GCC возвращает значение, соответствующее полному набору флагов Core i3/i5/i7, например, Haswell.

Проверить поколение вашего процессора можно следующей командой:

/lib/ld-linux-x86-64.so.2 --help | grep -B 3 -E "x86-64-v2"

После каждого поколения будет написано, поддерживается оно вашим процессором или нет. Например:

Subdirectories of glibc-hwcaps directories, in priority order:
x86-64-v4
x86-64-v3
x86-64-v2 (supported, searched)

Если у вас поддерживается хотя бы x86_64_v2, то вы так же сможете использовать данный репозиторий, ибо он предоставляет пакеты как для x86_64_v2, так и для x86_64_v3. Главное не перепутаете, какое именно у вас поколение.

Чтобы подключить репозиторий установим ключи для проверки подписей пакетов:

# Ключи для пакетов
git clone https://aur.archlinux.org/alhp-keyring.git
cd alhp-keyring
makepkg -sric --skippgpcheck

А также список зеркал:

git clone https://aur.archlinux.org/alhp-mirrorlist.git
cd alhp-mirrorlist
makepkg -sric

После этого нужно отредактировать конфиг pacman добавив репозиторий для нужной архитектуры (sudo nano /etc/pacman.conf).

Итак, если ваш процессор поддерживает только x86_64_v2 (как допустим у автора), то пишем следующее:

[core-x86-64-v2]
Include = /etc/pacman.d/alhp-mirrorlist

[extra-x86-64-v2]
Include = /etc/pacman.d/alhp-mirrorlist

[multilib-x86-64-v2]
Include = /etc/pacman.d/alhp-mirrorlist

[core]
Include = /etc/pacman.d/mirrorlist

[extra]
Include = /etc/pacman.d/mirrorlist

[multilib]
Include = /etc/pacman.d/mirrorlist

Если же у вас процессор поддерживает x86_64_v3, то пишем следующее:

[core-x86-64-v3]
Include = /etc/pacman.d/alhp-mirrorlist

[extra-x86-64-v3]
Include = /etc/pacman.d/alhp-mirrorlist

[multilib-x86-64-v3]
Include = /etc/pacman.d/alhp-mirrorlist

[core]
Include = /etc/pacman.d/mirrorlist

[extra]
Include = /etc/pacman.d/mirrorlist

[multilib]
Include = /etc/pacman.d/mirrorlist

После этого выполняем полное обновление системы:

sudo pacman -Syyuu

Перезагружаемся и наслаждаемся результатом (если вы все сделали правильно).