ROCm – DEV Community
NOTA: Este documento está basado en Ubuntu.
¿Todavía no sabes que es ROCm?
ROCm es la alternativa libre a CUDA. Es cierto que CUDA le lleva ventaja, pero, las cosas empiezan a cambiar, sobre todo debido a la deriva del mercado… Nvidia no puede “cubrir” la demanda mundial y ello a dado una ventana de oportunidad a que AMD pueda convencer.
ROCm son drivers libres para las GPU, aunque es evidente que al estar bajo el paraguas de AMD es sobretodo para AMDGPUs: https://rocm.docs.amd.com/en/latest/index.html
I. Diagnosticar tu AMDGPU
Lo primero es instalar las herramientas de diagnóstico necesarias:
sudo apt update && sudo apt install -y lshw
Y tras ello, ejecutamos el diagnóstico:
sudo lshw -c video 2>/dev/null | grep "configuration"
Para la validación del resultado debes buscar la cadena driver=amdgpu:
configuration: driver=amdgpu latency=0: El sistema gráfico está correctamente anclado al driver nativo.
configuration: driver=llvmpipe ... (O salida vacía / driver=unsigned): el sistema no está usando aceleración hardware.
Instalar/restaurar drivers nativos
A) Eliminar completamente instalaciones propietarias anteriores:
sudo amdgpu-install --uninstall 2>/dev/null
sudo apt purge -y "amdgpu-*" "rocm-*" "hsa-*"
B) Forzar la reinstalación del stack gráfico nativo de Ubuntu:
sudo apt update
sudo apt install --reinstall xserver-xorg-video-amdgpu xserver-xorg-core libgl1-mesa-dri
sudo apt install --reinstall linux-image-generic linux-headers-generic
C) Regenerar imagen de arranque y reiniciar:
sudo update-initramfs -u
sudo reboot
II. Configuración
Vamos a realizar la configuración para una iGPU Radeon, más exactamente configuramos un equipo con Ryzen AI 9 y Radeon 780M/880M.
sudo apt update && sudo apt install -y build-essential git curl cmake
UBUNTU_CODENAME=$(lsb_release -sc)
BASE_URL="https://repo.radeon.com/amdgpu-install/7.1.1/ubuntu/$UBUNTU_CODENAME/"
INSTALLER_FILE=$(curl -s "$BASE_URL" | grep -o 'amdgpu-install_[0-9.-]*_all.deb' | sort -V | tail -n 1)
if [ -z "$INSTALLER_FILE" ]; then
echo "Error Crítico: No se localizó el paquete instalador en $BASE_URL"
exit 1
fi
wget -c "$BASE_URL$INSTALLER_FILE"
sudo apt install -y "./$INSTALLER_FILE"
# Desplegando pila ROCm y HIP (User-space).
# El flag --no-dkms evita la recompilación del módulo del kernel, previniendo conflictos gráficos.
sudo amdgpu-install --usecase=rocm,hip --no-dkms -y
sudo usermod -aG render,video $USER
# Inyección de variable de entorno para compatibilidad con RDNA 3.5
if ! grep -q "HSA_OVERRIDE_GFX_VERSION=11.0.0" ~/.bashrc; then
echo 'export HSA_OVERRIDE_GFX_VERSION=11.0.0' >> ~/.bashrc
fi
III. Comprobaciones y tests
Primero revisemos que tenemos la GPU:
lspci | grep -i amd
Deberías ver tu gráfica en el listado.
Si tienes memoria compartida (shared memory) deberás habilitar el máximo que necesites en tu BIOS (UMA Frame Buffer Size o Integrated Graphics Memory).
Preparar un contenedor para las comprobaciones.
Crea un Dockerfile:
FROM rocm/pytorch:rocm6.1_ubuntu22.04_py3.10_pytorch_2.1.2
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
rocm-smi \
git \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
ENV HSA_OVERRIDE_GFX_VERSION=11.0.0
WORKDIR /app
COPY test_gpu.py .
CMD ["/bin/bash", "-c", "python3 test_gpu.py; bash"]
… y para facilitar el uso, también creamos un compose.yml:
services:
rocm-strix:
build: .
image: rocm-strix:v1
container_name: rocm_strix_container
restart: unless-stopped
# Fundamental para el acceso al hardware
devices:
- "/dev/kfd:/dev/kfd"
- "/dev/dri:/dev/dri"
# Permisos de seguridad relajados para acceso a memoria GPU
security_opt:
- seccomp:unconfined
cap_add:
- SYS_PTRACE
# Grupos necesarios para acceder a video/render
group_add:
- video
- render
# Vital para PyTorch y sus DataLoaders (memoria compartida)
ipc: host
environment:
# CRÍTICO: Engaña a la librería para usar kernels de RX 7900 en la APU que estoy testeando
- HSA_OVERRIDE_GFX_VERSION=11.0.0
- ROCM_PATH=/opt/rocm
# Opcional: Define la zona horaria
- TZ=Europe/Madrid
volumes:
- ./data:/app/data
# --- Acceso al Hardware ---
- /sys/class/power_supply:/sys/class/power_supply:ro
- /sys/class/drm:/sys/class/drm:ro
- /sys/class/hwmon:/sys/class/hwmon:ro
Y por último un pequeño script, test_gpu.py:
import torch
import time
import psutil
import platform
import os
import subprocess
import json
def format_bytes(size):
power = 2**10
n = 0
power_labels = 0 : 'B', 1: 'KB', 2: 'MB', 3: 'GB', 4: 'TB'
while size > power:
size /= power
n += 1
return f"size:.2f power_labels[n]"
def get_cpu_info():
try:
command = "cat /proc/cpuinfo | grep 'model name' | uniq | cut -d : -f 2"
cpu_name = subprocess.check_output(command, shell=True).decode().strip()
except:
cpu_name = platform.processor()
logical = psutil.cpu_count(logical=True)
physical = psutil.cpu_count(logical=False)
freq = psutil.cpu_freq()
freq_str = f"freq.max:.0f Mhz" if freq else "Unknown"
return cpu_name, physical, logical, freq_str
def get_battery_status():
if not hasattr(psutil, "sensors_battery"): return "No soportado"
try:
battery = psutil.sensors_battery()
if battery is None: return "No detectada"
plugged = "CONECTADO" if battery.power_plugged else "BATERÍA"
return f"battery.percent% (plugged)"
except: return "Error leyendo batería"
def get_gpu_sensors_rocm():
data =
try:
# Intentamos obtener JSON. Si falla, manejamos la excepción.
raw_json = subprocess.check_output("rocm-smi -a --json", shell=True).decode()
smi_data = json.loads(raw_json)
card = list(smi_data.keys())[0]
gpu_data = smi_data[card]
# --- LÓGICA DE NOTAS EXPLICATIVAS ---
# 1. TEMPERATURAS
edge = gpu_data.get("Temperature (Sensor edge) (C)", "N/A")
junc = gpu_data.get("Temperature (Sensor junction) (C)", "N/A")
if junc == "N/A": junc = "N/A (APU: Solo reporta sensor Edge)"
data['temp'] = f"edge°C (Edge) / junc"
# 2. CONSUMO (POWER)
pwr = gpu_data.get("Average Graphics Package Power (W)", "N/A")
if pwr == "N/A": pwr = "N/A (APU: Energía compartida CPU/GPU)"
data['power'] = pwr
# 3. RELOJES (CLOCKS)
sclk = gpu_data.get("SCLK", "N/A")
mclk = gpu_data.get("MCLK", "N/A")
if sclk == "N/A" or mclk == "N/A":
# Si Docker bloquea uno, bloquea los dos. Ponemos un mensaje global.
data['clocks'] = "N/A / N/A (Docker: Ambos sensores (SCLK/MCLK) bloqueados por aislamiento)"
else:
data['clocks'] = f"sclk / mclk"
# 4. VENTILADOR
fan = gpu_data.get("Fan Speed (%)", "N/A")
if fan == "N/A": fan = "N/A (Laptop: Controlado por BIOS/EC)"
data['fan'] = fan
# 5. CARGA Y MEMORIA
data['usage'] = gpu_data.get("GPU use (%)", "N/A")
data['vram_used'] = gpu_data.get("VRAM Total Used Memory (B)", 0)
data['gtt_used'] = gpu_data.get("GTT Total Used Memory (B)", 0)
except Exception as e:
data['error'] = f"Error interpretando sensores: str(e)"
# Valores por defecto seguros para que no rompa el print
data['temp'] = "Error"
data['power'] = "Error"
data['clocks'] = "Error"
data['fan'] = "Error"
data['usage'] = "?"
data['vram_used'] = 0
data['gtt_used'] = 0
return data
def benchmark_latency(iters=1000):
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
for _ in range(100): torch.cuda.synchronize() # Warmup
start.record()
for _ in range(iters): torch.cuda.synchronize()
end.record()
torch.cuda.synchronize()
return (start.elapsed_time(end) / iters) * 1000
def benchmark_bandwidth(size_mb=512):
elements = size_mb * 1024 * 1024 // 4
t_cpu = torch.randn(elements, dtype=torch.float32)
torch.cuda.synchronize()
# H2D
start = time.time()
t_gpu = t_cpu.to('cuda')
torch.cuda.synchronize()
h2d = (size_mb / 1024) / (time.time() - start)
# D2H
start = time.time()
_ = t_gpu.to('cpu')
torch.cuda.synchronize()
d2h = (size_mb / 1024) / (time.time() - start)
return h2d, d2h
def benchmark_compute(n, dtype):
a = torch.randn(n, n, device="cuda", dtype=dtype)
b = torch.randn(n, n, device="cuda", dtype=dtype)
torch.mm(a, b)
torch.cuda.synchronize()
start = time.time()
torch.mm(a, b)
torch.cuda.synchronize()
ops = 2 * (n ** 3)
return (ops / (time.time() - start)) / 1e12
# ================= REPORT =================
print("\n" + "="*60)
print(" ROCm ULTIMATE DIAGNOSTIC TOOL ")
print("="*60)
# [1] HOST
cpu, _, _, _ = get_cpu_info()
vm = psutil.virtual_memory()
print(f"\n[1] HOST & ENERGÍA")
print(f"CPU: cpu")
print(f"RAM Sistema: format_bytes(vm.available) libres / format_bytes(vm.total) Total")
print(f"Estado Energía: get_battery_status()")
# [2] GPU
print(f"\n[2] SENSORES GPU (ROCm SMI)")
if torch.cuda.is_available():
sensors = get_gpu_sensors_rocm()
d = 0
props = torch.cuda.get_device_properties(d)
print(f"Dispositivo: torch.cuda.get_device_name(d)")
print(f"VRAM Total: format_bytes(props.total_memory)")
print(f"-"*40)
print(f"Temp: sensors.get('temp', '?')")
print(f"Consumo: sensors.get('power', '?')")
print(f"Relojes: sensors.get('clocks', '?')")
print(f"Ventilador: sensors.get('fan', '?')")
print(f"-"*40)
print(f"Carga GPU: sensors.get('usage', '?')%")
print(f"Memoria Usada: VRAM: format_bytes(float(sensors.get('vram_used', 0))) | GTT: format_bytes(float(sensors.get('gtt_used', 0)))")
else:
print("FATAL: GPU NO DETECTADA")
# [3] BENCHMARKS
print(f"\n[3] PRUEBAS DE ESTRÉS & RENDIMIENTO")
print(f"Latencia Kernel: benchmark_latency():.2f µs")
try:
h2d, d2h = benchmark_bandwidth(512)
print(f"Ancho Banda: Escritura: h2d:.2f GB/s | Lectura: d2h:.2f GB/s")
except: print("Error memoria")
print("\n--- Potencia de Cómputo ---")
for size in [4096, 8192]:
tf32 = benchmark_compute(size, torch.float32)
print(f"Matriz sizexsize: FP32: tf32:.2f TFLOPS", end="")
try:
tf16 = benchmark_compute(size, torch.float16)
print(f" | FP16: tf16:.2f TFLOPS (Boost: tf16/tf32:.2fx)")
except: print("")
print("\n" + "-"*60 + "\n")
Y ahora, para probarlo:
docker compose build
docker compose up
Si algo falla y necesitas recrear el contenedor, debes eliminarlo previamente con:
docker rm -f rocm_strix_container
Por último, puedes ver el estado de la GPU con:
rocm-smi
y del sistema con:
htop
