AI и DIY,  Майнинг,  Статьи

Разгон и даунвольтинг видеокарт 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-задач, играх и не только.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *