
Разгон и даунвольтинг видеокарт Nvidia в Linux
Чтобы управлять на видеокартах Nvidia мощностью, разгоном по памяти и ядру, а также скоростью вращения вентиляторов в linux часто используют утилиты nvidia-settings, nvidia-smi и команды:
sudo nvidia-smi -pm 1
sudo nvidia-smi -i 0 -pl 100
sudo nvidia-settings -a '[gpu:0]/GPUFanControlState=1'
sudo nvidia-settings -a '[fan:0]/GPUTargetFanSpeed=35'
sudo nvidia-settings -a '[gpu:0]/GPUMemoryTransferRateOffset[3]=450'
sudo nvidia-settings -a '[gpu:0]/GPUGraphicsClockOffset[3]=130'
sudo nvidia-settings -a '[gpu:0]/GPUPowerMizerMode=1'
В этом случае скорость вращения вентиляторов устанавливается на заданное значение, которое не всегда является оптимальным. В простое видеокарты создают лишний шум, происходит выработка ресурса подшипников, а в режиме максимальной нагрузки на GPU, наоборот, такого потока воздуха может не хватать.
Для решения проблемы можно использовать скрипт, задействующий библиотеку nvml. При этом можно будет добиться не только управления частотами и скоростью вращения fan, но и обеспечить даунвольтинг GPU, что увеличит RGL при их работе.

В gentoo для работы с nvml удобно задействовать python-обертку dev-python/nvidia-ml-py:

Ее можно установить командой:
sudo emerge --ask dev-python/nvidia-ml-py
Чтобы управлять видеокартами Nvidia, нужно создать два файла:
- /usr/local/bin/nv-profile.py — скрипт на python, в котором задаются параметры разгона, Power Limit, скорость вращения вентиляторов и другие параметры;
- /etc/init.d/nv-undervolt — для запуска службы управления видеокартами Nvidia в Linux.
В каталоге /usr/local/bin/ создаем файл nv-profile.py:
sudo nano /usr/local/bin/nv-profile.py
и вставляем в него текст скрипта управления (для системы инициализации OpenRC):
#!/usr/bin/env python3
import sys
import time
import os
import glob
import subprocess
from pynvml import *
def get_x_auth():
# Динамический поиск файла авторизации X-сервера для LightDM/root
possible_paths = [
"/var/run/lightdm/root/:0",
"/var/run/lightdm/root/*",
"/root/.Xauthority"
]
for path in possible_paths:
files = glob.glob(path)
if files and os.path.exists(files[0]):
return files[0]
return None
def wait_for_xorg():
print("Ожидание инициализации X-сервера...")
for _ in range(30):
auth = get_x_auth()
if auth:
res = subprocess.run(
f"DISPLAY=:0 XAUTHORITY={auth} nvidia-settings -q gpus",
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
if res.returncode == 0:
print(f"X-сервер готов. Ипользуется XAUTHORITY: {auth}")
time.sleep(2)
return auth
time.sleep(1)
print("Внимание: X-сервер не найден, разгон памяти и управление кулерами могут быть недоступны.", file=sys.stderr)
return None
def run_nv_setting(auth, command):
if not auth:
return
try:
subprocess.run(
f"DISPLAY=:0 XAUTHORITY={auth} nvidia-settings -c :0 {command}",
shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
except Exception:
pass
def calculate_fan_speed(temp):
if temp <= 40: return 25
elif temp <= 50: return 50
elif temp <= 55: return 70
elif temp <= 59: return 85
else: return 100
def monitor_and_cool(device_count, auth):
print("Запуск фонового мониторинга температуры...")
try:
while True:
for i in range(device_count):
device = nvmlDeviceGetHandleByIndex(i)
temp = nvmlDeviceGetTemperature(device, NVML_TEMPERATURE_GPU)
target_fan_speed = calculate_fan_speed(temp)
run_nv_setting(auth, f"-a '[gpu:{i}]/GPUFanControlState=1'")
run_nv_setting(auth, f"-a '[fan:{i}]/GPUTargetFanSpeed={target_fan_speed}'")
time.sleep(2)
except KeyboardInterrupt:
pass
def main():
if len(sys.argv) < 2 or sys.argv[1] not in ['apply', 'reset']:
print("Использование: nv-profile.py [apply|reset]", file=sys.stderr)
sys.exit(1)
action = sys.argv[1]
try:
nvmlInit()
device_count = nvmlDeviceGetCount()
if action == 'apply':
# Находим актуальный токен авторизации графики
auth = wait_for_xorg()
print(f"Обнаружено видеокарт Nvidia: {device_count}")
for i in range(device_count):
device = nvmlDeviceGetHandleByIndex(i)
# 1. Лимит мощности (100 Вт) через ПРЯМОЙ НАДЁЖНЫЙ API NVML (Ватты переводятся в милливатты)
try:
nvmlDeviceSetPowerManagementLimit(device, 100 * 1000)
print(f"[{i}] Нативный лимит мощности 100W успешно установлен.")
except Exception as e:
subprocess.run(f"nvidia-smi -i {i} -pl 100", shell=True, stdout=subprocess.DEVNULL)
# 2. Разгон ЯДРА (+100 МГц) и ПАМЯТИ (+500 МГц) через nvidia-settings
run_nv_setting(auth, f"-a '[gpu:{i}]/GPUGraphicsClockOffset=100'")
run_nv_setting(auth, f"-a '[gpu:{i}]/GPUMemoryTransferRateOffset=500'")
print(f"[{i}] Профиль применен: PL=100W, Core Offset=+100MHz, Mem=+500MHz")
monitor_and_cool(device_count, auth)
elif action == 'reset':
auth = get_x_auth()
for i in range(device_count):
device = nvmlDeviceGetHandleByIndex(i)
# Сброс Power Limit на заводской через NVML
try:
default_pl = nvmlDeviceGetPowerManagementDefaultLimit(device)
nvmlDeviceSetPowerManagementLimit(device, default_pl)
except:
subprocess.run(f"nvidia-smi -i {i} -pl 0", shell=True, stdout=subprocess.DEVNULL)
# Сброс частот и кулеров
run_nv_setting(auth, f"-a '[gpu:{i}]/GPUFanControlState=0'")
run_nv_setting(auth, f"-a '[gpu:{i}]/GPUGraphicsClockOffset=0'")
run_nv_setting(auth, f"-a '[gpu:{i}]/GPUMemoryTransferRateOffset=0'")
print(f"[{i}] Настройки сброшены.")
nvmlShutdown()
except Exception as e:
print(f"Ошибка: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()
Делаем файл исполняемым:
sudo chmod +x /usr/local/bin/nv-profile.py
Создаем службу для запуска скрипта управления видеокартами:
sudo nano /etc/init.d/nv-undervolt
вносим туда следующий код:
#!/sbin/openrc-run
# /etc/init.d/nv-undervolt
description="Undervolt, Overclock and Fan Control for Dual NVIDIA GPUs"
command="/usr/local/bin/nv-profile.py"
command_args="apply"
command_background="yes"
pidfile="/run/${RC_SVCNAME}.pid"
depend() {
# Служба требует загруженных модулей ядра
need modules
# Ждем, пока запустится менеджер дисплея (X-сервер)
after display-manager
}
stop_post() {
# Этот блок гарантированно выполнится ПОСЛЕ остановки фонового демона.
# Он вернет вентиляторы и частоты к заводским настройкам, даже если
# служба ранее упала или PID-файл не создался.
/usr/local/bin/nv-profile.py reset >/dev/null 2>&1
return 0
}

Делаем файл запуска службы nv-undervolt исполняемым:
sudo chmod +x /etc/init.d/nv-undervolt
Запускаем службу:
sudo rc-service nv-undervolt start
Добавляем службу разгона и даунвольта в уровень запуска default, чтобы она стартовала сама при включении ПК:
sudo rc-update add nv-undervolt default
При изменени параметров работы видеокарт в файле /usr/local/bin/nv-profile.py изменения можно применить без перезагрузки командой:
sudo rc-service nv-undervolt restart
При успешном выполнении команды в терминале появятся строки:
* Stopping nv-undervolt ... [ ok ]
* Starting nv-undervolt ... [ ok ]
Отслеживать состояние видеокарт можно командой:
nvidia-smi --query-gpu=index,clocks.current.graphics,clocks.current.memory,power.draw,temperature.gpu,fan.speed --format=csv
0, 139 MHz, 405 MHz, 5.88 W, 32, 25 %
1, 139 MHz, 405 MHz, 5.15 W, 28, 25 %
0, 139 MHz, 405 MHz, 5.98 W, 32, 25 %
1, 139 MHz, 405 MHz, 5.49 W, 28, 25 %
0, 139 MHz, 405 MHz, 5.98 W, 32, 25 %
1, 139 MHz, 405 MHz, 5.49 W, 28, 25 %
0, 139 MHz, 405 MHz, 5.98 W, 32, 25 %
1, 139 MHz, 405 MHz, 5.15 W, 28, 25 %
...
либо воспользоваться утилитой nvtop (в gentoo — sys-process/nvtop):

Поведение видеокарты в linux можно отслеживать в параметрах Nvidia X-сервера:

Если служба nv-undervolt по какой-то причине зависает, можно заставить OpenRC сбросить ее в состояние stop командой:
sudo rc-service nv-undervolt zap
затем можно ее перезагрузить командой:
sudo rc-service nv-undervolt start
В описанном способе управления параметрами работы видеокарт Nvidia Pascal даунвольтинг осуществляется косвенным методом за счет сдвига кривой частот/напряжений в сочетании с ограничением теплопакета (PL). При этом, для достижения одной и той же частоты чипу видеокарты требуется меньшее напряжение. При обычном разгоне, когда используется оффсет +100 МГц, карта автоматически поднимает и напряжение ядра. Благодаря использованию PL=100, происходит автоматический сброс вольтажа до примерно 0.9В при одновременном сохранении повышенной частоты, что обеспечивает даунвольтинг (по крайнейй мере, теоретически). Это значительно (на 5-10 градусов) снижает нагрев GPU, что особенно актуально в летнее время…
Описанная выше методика актуальна для видеокарт Nvidia Pascal, которые не понимают команд nvmlDeviceSetGpcClkVfOffset и nvmlDeviceSetGpuLockedClocks, поддержка которых появилась в архитектуре Turing.
Разгон и даунвольтинг видеокарт Nvidia на linux-сервере
Чтобы полностью избавиться от использования утилиты nvidia-settings и зависимостей от графического X-сервера (LightDM) для работы на сервере без X (в консоли TTY) сразу после загрузки ОС, можно использовать методику из статьи или вышеуказаный способ, модифифцировав код файла /usr/local/bin/nv-profile.py:
#!/usr/bin/env python3
import sys
import time
from pynvml import *
def calculate_fan_speed(temp):
# Кривая охлаждения для удержания температуры видеокарт в пределах 59 градусов
if temp <= 40:
return 25 # Минимальная скорость 25% (тихий режим для простоя)
elif temp <= 50:
return 50 # Начало нагрузки
elif temp <= 55:
return 70 # Усиленный обдув
elif temp <= 59:
return 85 # Предкритическая зона (максимальное удержание до 59 градусов)
else:
return 100 # Аварийный режим при сильном нагреве карт
def monitor_and_cool(device_count):
print("Запуск фонового мониторинга температуры и управления кулерами...")
try:
while True:
for i in range(device_count):
device = nvmlDeviceGetHandleByIndex(i)
temp = nvmlDeviceGetTemperature(device, NVML_TEMPERATURE_GPU)
target_fan_speed = calculate_fan_speed(temp)
# Нативное управление кулерами через NVML
# Включаем ручной режим управления вентилятором (индекс 0)
try:
# Для некоторых карт Pascal ручное управление fan через NVML может требовать
# поддержки со стороны конкретной версии драйвера.
nvmlDeviceSetFanControlPolicy(device, 0, NVML_FAN_POLICY_MANUAL)
nvmlDeviceSetFanSpeed_v2(device, 0, target_fan_speed)
except Exception:
# Резервный вызов для более ранних версий API
try:
nvmlDeviceSetFanSpeed(device, target_fan_speed)
except:
pass
time.sleep(2)
except KeyboardInterrupt:
pass
def main():
if len(sys.argv) < 2 or sys.argv[1] not in ['apply', 'reset']:
print("Использование: nv-profile.py [apply|reset]", file=sys.stderr)
sys.exit(1)
action = sys.argv[1]
try:
nvmlInit()
device_count = nvmlDeviceGetCount()
if action == 'apply':
print(f"Обнаружено видеокарт Nvidia: {device_count}")
for i in range(device_count):
device = nvmlDeviceGetHandleByIndex(i)
# 1. Нативный лимит мощности (100 Вт). Функция ожидает милливатты (100 * 1000)
nvmlDeviceSetPowerManagementLimit(device, 100000)
# 2. Даунвольт ядра: задаем оффсет +110 МГц (сдвиг кривой вольтажа)
try:
nvmlDeviceSetGpcClkVfOffset(device, 110)
except Exception as e:
print(f"[{i}] Предупреждение по оффсету ядра: {e}")
# 3. Разгон памяти: задаем оффсет +500 МГц через чистый NVML
try:
nvmlDeviceSetMemoryClkOffset(device, 500)
except Exception as e:
print(f"[{i}] Предупреждение по оффсету памяти: {e}")
print(f"[{i}] Чистый NVML профиль применен: PL=100W, Core Offset=+110MHz, Mem Offset=+500MHz")
# Переход в бесконечный цикл контроля температуры кулеров
monitor_and_cool(device_count)
elif action == 'reset':
for i in range(device_count):
device = nvmlDeviceGetHandleByIndex(i)
# Сброс Power Limit на заводское значение по умолчанию
try:
default_pl = nvmlDeviceGetPowerManagementDefaultLimit(device)
nvmlDeviceSetPowerManagementLimit(device, default_pl)
except:
pass
# Сброс оффсетов ядра и памяти в 0
try:
nvmlDeviceSetGpcClkVfOffset(device, 0)
nvmlDeviceSetMemoryClkOffset(device, 0)
except:
pass
# Возврат вентиляторов в автоматический режим управления (заводской BIOS)
try:
nvmlDeviceSetFanControlPolicy(device, 0, NVML_FAN_POLICY_TEMPERATURE_CONTROLLER)
except:
pass
print(f"[{i}] Все настройки сброшены к заводским через NVML.")
nvmlShutdown()
except Exception as e:
print(f"Критическая ошибка NVML: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()
Применяем настройки в текущей сессии:
sudo rc-service nv-undervolt restart
Проверяем работу:
nvidia-smi --query-gpu=index,clocks.current.graphics,clocks.current.memory,power.draw,temperature.gpu,fan.speed --format=csv -l 1
0, 1506 MHz, 3802 MHz, 100.88 W, 42, 50 %
1, 1544 MHz, 3802 MHz, 99.67 W, 42, 50 %
0, 1506 MHz, 3802 MHz, 98.63 W, 43, 50 %
1, 1569 MHz, 3802 MHz, 95.61 W, 42, 50 %
0, 1506 MHz, 3802 MHz, 97.70 W, 43, 50 %
Благодаря работе на уровне системного драйвера и ядра последний вариант скрипта будет работать и в графическом окружении, причем более надежно, чем вариант, рассмотренный выше. Настройки даунвольта и разгона видеокарт Nvidia на архитектуре Pascal не сбросятся даже при выходе из учетной записи пользователя, блокировке экрана и перезапуске LightDM (rc-service lightdm restart).
В init-скрипте запуска OpenRC на сервере можно убрать зависимость after display-manager, приведя код файла /etc/init.d/nv-undervolt к следующему виду:
#!/sbin/openrc-run
description="Pure NVML Undervolt, Overclock and Fan Control for Dual GPUs"
command="/usr/local/bin/nv-profile.py"
command_args="apply"
command_background="yes"
pidfile="/run/${RC_SVCNAME}.pid"
depend() {
# Требуются только загруженные модули ядра nvidia
need modules
}
stop_post() {
# Гарантированный сброс параметров видеокарт при остановке службы
/usr/local/bin/nv-profile.py reset >/dev/null 2>&1
return 0
}

Указанные способы управления работой видеокарт Nvidia поколения Pascal позволяют более эффективно их использовать в майнинге:

а также при работе с llama.cpp и выполнении других AI-задач, играх и не только.


