-
Exponer menos o más RAM hacia Vulkan usando iGPUs
Cuando usamos una gráfica integrada AMD para ejecutar modelos LLM mediante Vulkan, una de las primeras dudas que aparece es bastante lógica: si nuestra iGPU no tiene VRAM dedicada real, ¿de dónde sale la memoria que Vulkan dice que puede usar? La respuesta corta es que, en una APU o procesador AMD con gráfica integrada, Vulkan no está viendo una VRAM física independiente como ocurriría en una tarjeta gráfica dedicada. Lo que ve es una combinación de memoria UMA preasignada por la BIOS + memoria del sistema que el driver AMDGPU puede mapear para que la GPU la use. Esto es especialmente importante cuando queremos ejecutar modelos grandes con herramientas como llama.cpp, porque el tamaño del modelo, la cuantización, el contexto y la cantidad de capas que descargamos a la GPU pueden depender directamente de la memoria que Vulkan ve como disponible.
El problema práctico
Imaginemos un portátil o un mini PC con un procesador AMD moderno con gráfica integrada, por ejemplo un Ryzen con iGPU Radeon compatible con Vulkan. En Linux instalamos los drivers Mesa/RADV, compilamos llama.cpp con soporte Vulkan y comprobamos los dispositivos disponibles.
./llama-cli --list-devices
Podríamos ver algo como esto:
Available devices: Vulkan0: AMD Radeon Graphics (RADV GFX1152) (33016 MiB, 32471 MiB free)
La lectura rápida sería pensar: “mi iGPU tiene 32 GB de VRAM”. Pero eso no es correcto. Una iGPU no tiene esos 32 GB de VRAM física dedicada. Lo que ocurre es que Vulkan está viendo un presupuesto de memoria expuesto por el driver gráfico. Ese presupuesto puede cambiar si cambiamos en la BIOS el tamaño de memoria reservado para la gráfica integrada. Por ejemplo, el mismo equipo puede mostrar una cifra menor con la memoria de la iGPU en automático y una cifra mayor si reservamos manualmente 8 GB, 16 GB o el valor que permita la BIOS.
Los tres conceptos importantes: UMA, VRAM y GTT
Para entender esto tenemos que separar tres conceptos que suelen mezclarse:
- El primero es UMA. En muchas BIOS aparece como UMA Frame Buffer Size, iGPU Memory, Integrated Graphics Share Memory, Share Memory o algún nombre parecido. Es la cantidad de RAM del sistema que el firmware reserva desde el arranque para la gráfica integrada. Si tenemos 64 GB de RAM y reservamos 16 GB como UMA para la iGPU, Linux ya no podrá usar esos 16 GB como RAM normal. Esa memoria queda apartada para la gráfica integrada.
- El segundo concepto es VRAM, pero en una iGPU debemos interpretarlo con cuidado. Cuando el driver AMDGPU dice que tenemos 2 GB, 4 GB, 8 GB o 16 GB de VRAM en una iGPU, normalmente se refiere a esa memoria UMA preasignada por BIOS, no a chips de VRAM dedicados físicamente en la placa.
- El tercer concepto es GTT. En AMDGPU, GTT es memoria del sistema que el driver puede mapear para uso de la GPU. No es VRAM preasignada, pero puede ser accesible por la GPU mediante el sistema de traducción y gestión de memoria del driver.
Por eso, en una iGPU AMD, lo que Vulkan acaba viendo puede parecerse mucho a esta suma:
Memoria visible para Vulkan ≈ VRAM/UMA preasignada + GTT
No es una fórmula universal perfecta para todos los equipos, BIOS, kernels y versiones de Mesa, pero en la práctica explica muy bien el comportamiento que observamos en muchos sistemas AMD con gráfica integrada.
Cómo comprobarlo en Linux
Lo primero es comprobar qué dispositivo Vulkan ve nuestro sistema.
vulkaninfo --summary
En un sistema AMD con Mesa/RADV deberíamos ver algo parecido a esto:
Devices: ======== GPU0: deviceType = PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU deviceName = AMD Radeon Graphics driverID = DRIVER_ID_MESA_RADV driverName = radv
Si usamos llama.cpp, también podemos comprobarlo con:
./llama-cli --list-devices
La salida puede ser algo como:
Available devices: Vulkan0: AMD Radeon Graphics (RADV GFX1152) (33016 MiB, 32471 MiB free)
Ahora necesitamos ver qué dice realmente el kernel sobre VRAM y GTT. Para eso podemos revisar el arranque del driver AMDGPU:
sudo dmesg | grep -Ei 'amdgpu.*(VRAM|GTT|memory|TTM|gart)'
En un caso real podemos obtener esto:
[ 2.244407] amdgpu 0000:c5:00.0: amdgpu: VRAM: 2048M 0x0000008000000000 - 0x000000807FFFFFFF (2048M used) [ 2.244409] amdgpu 0000:c5:00.0: amdgpu: GART: 512M 0x00007FFF00000000 - 0x00007FFF1FFFFFFF [ 2.244519] [drm] amdgpu: 2048M of VRAM memory ready [ 2.244521] [drm] amdgpu: 30968M of GTT memory ready.
Aquí ya no estamos adivinando nada. El kernel nos está diciendo exactamente cuánto expone el driver como VRAM y cuánto como GTT.
VRAM: 2048 MiB GTT: 30968 MiB
Si sumamos ambas cifras:
2048 + 30968 = 33016 MiB
Y eso coincide exactamente con lo que mostraba llama.cpp:
Vulkan0: AMD Radeon Graphics (RADV GFX1152) (33016 MiB, 32471 MiB free)
Por tanto, en este caso concreto, Vulkan está viendo 33016 MiB porque AMDGPU está exponiendo 2048 MiB de VRAM UMA más 30968 MiB de GTT.
Qué hace la opción Auto de la BIOS
La opción Auto de la BIOS no significa que la iGPU pueda usar mágicamente toda la RAM que quiera. Significa que el firmware decide cuánta RAM reserva como UMA para la gráfica integrada. En algunos portátiles, esta decisión automática depende de la cantidad total de RAM instalada. Por ejemplo, en un equipo concreto podemos encontrar una política de este estilo:
RAM instalada entre 8 GB y 16 GB -> UMA automática de 512 MB RAM instalada entre 16 GB y 32 GB -> UMA automática de 1 GB RAM instalada por encima de 32 GB -> UMA automática de 2 GB
Esto no es una ley universal de AMD. Es una política concreta de algunos fabricantes y puede variar entre portátiles, placas base, mini PCs y servidores. Lo importante es entender que la opción Auto puede cambiar según la cantidad de RAM instalada. Si nuestro portátil trae 16 GB de RAM, Auto podría reservar 512 MB o 1 GB para la iGPU, según el fabricante. Si ampliamos el equipo a 32 GB o 64 GB, la misma opción Auto podría pasar a reservar 2 GB. Y si quitamos un módulo de RAM o cambiamos la combinación de módulos, el comportamiento automático puede cambiar otra vez. Esto explica por qué dos usuarios con la misma CPU AMD y la misma iGPU pueden ver cantidades distintas en Vulkan. No solo importa el modelo de procesador. También importan la BIOS, el firmware, la RAM instalada, la política automática del fabricante, el kernel, Mesa y el driver AMDGPU.
El papel de GTT
La segunda parte de la suma es GTT. En muchos sistemas Linux con AMDGPU, el tamaño de GTT suele estar relacionado con la cantidad de RAM que Linux ve como disponible o utilizable. En la práctica, es habitual ver que GTT se aproxima a la mitad de la RAM visible para Linux, aunque el cálculo exacto depende del kernel, TTM, el driver y las reservas internas.
Supongamos un equipo con 64 GB de RAM física. Si la BIOS reserva poca memoria UMA para la iGPU, Linux puede ver alrededor de 60 GiB de RAM total. Si el driver acaba exponiendo una GTT aproximada a la mitad de esa RAM visible, podríamos ver algo cercano a 30 GiB de GTT. Eso es justo lo que ocurre en este ejemplo real:
Memoria visible en Linux: 60 GiB VRAM UMA en Auto: 2048 MiB GTT: 30968 MiB Vulkan total: 33016 MiB
La cuenta vuelve a cuadrar:
2 GiB de UMA + unos 30 GiB de GTT = unos 32 GiB visibles para Vulkan
Qué pasa si reservamos memoria manualmente en BIOS
Ahora viene la parte interesante para los LLMs. Si en la BIOS dejamos UMA en Auto, el firmware puede reservar solo 2 GB para la gráfica integrada. Pero si la BIOS nos deja fijar manualmente 4 GB, 8 GB o 16 GB, la parte VRAM/UMA de la suma aumenta. El coste es que Linux verá menos RAM general, porque esa memoria queda apartada para la iGPU desde el arranque.
Volvamos al ejemplo de un equipo con 64 GB de RAM física. Si dejamos Auto y el firmware reserva 2 GB para la iGPU, podríamos acabar con algo parecido a esto:
UMA/VRAM BIOS: 2 GiB RAM visible Linux: ~60 GiB GTT aproximada: ~30 GiB Vulkan visible: ~32 GiB
Si configuramos manualmente 4 GB de UMA:
UMA/VRAM BIOS: 4 GiB RAM visible Linux: ~58 GiB GTT aproximada: ~29 GiB Vulkan visible: ~33 GiB
Si configuramos manualmente 8 GB de UMA:
UMA/VRAM BIOS: 8 GiB RAM visible Linux: ~54 GiB GTT aproximada: ~27 GiB Vulkan visible: ~35 GiB
Si configuramos manualmente 16 GB de UMA:
UMA/VRAM BIOS: 16 GiB RAM visible Linux: ~48 GiB GTT aproximada: ~24 GiB Vulkan visible: ~40 GiB
La lógica es clara: al aumentar UMA, sube la parte fija que Vulkan puede ver como VRAM. Al mismo tiempo, baja la RAM visible para Linux y, por tanto, puede bajar también la GTT calculada por el driver. Pero la suma total puede seguir aumentando. En el caso anterior:
Auto: 2 GiB UMA + 30 GiB GTT = 32 GiB Vulkan 16 GB manual: 16 GiB UMA + 24 GiB GTT = 40 GiB Vulkan
Por eso, aunque reservar 16 GB en BIOS reduce la RAM general disponible para Linux, puede aumentar la memoria total que Vulkan ve para la iGPU.
Por qué esto puede importar al cargar modelos LLM
Cuando ejecutamos un modelo LLM con llama.cpp y backend Vulkan, podemos indicar cuántas capas del modelo queremos descargar a GPU con el parámetro -ngl.
./llama-cli \ --device Vulkan0 \ -m /ruta/al/modelo.gguf \ -p "Hola" \ -ngl 30 \ -t "$(nproc)"
El parámetro -ngl significa número de capas descargadas a GPU. Si usamos -ngl 0, no descargamos capas del modelo a GPU. Si usamos -ngl 10, descargamos 10 capas. Si usamos -ngl 30 y el modelo tiene 30 capas, intentamos descargar todas las capas principales del modelo a Vulkan.
Para ver cuántas capas tiene un modelo GGUF podemos inspeccionar sus metadatos. En llama.cpp podemos usar el script gguf_dump.py:
python3 llama.cpp/gguf-py/gguf/scripts/gguf_dump.py --no-tensors /ruta/al/modelo.gguf | grep -E "block_count|general.name|general.architecture"
Una salida posible sería:
general.architecture = 'gemma4' general.name = 'Google Gemma 4 26B A4B It' gemma4.block_count = 30
Eso nos dice que el modelo tiene 30 bloques o capas principales. Por tanto, en ese modelo concreto:
-ngl 0 -> CPU para las capas del modelo -ngl 10 -> 10 capas en Vulkan y el resto en CPU -ngl 20 -> 20 capas en Vulkan y el resto en CPU -ngl 30 -> todas las capas principales en Vulkan -ngl 99 -> intenta meter todas, pero no aporta nada frente a 30 si el modelo solo tiene 30
Si Vulkan ve 32 GiB y el modelo necesita más memoria para descargar todas las capas, puede fallar. Si ajustamos la BIOS y Vulkan pasa a ver 40 GiB, quizá ese mismo modelo sí pueda cargarse completo por Vulkan. Esto no significa que siempre debamos reservar más UMA. Significa que debemos entender el reparto y medir.
Cómo decidir si nos conviene Auto o manual
Si usamos el equipo para tareas generales, escritorio, navegación, virtualización ligera, desarrollo y algún modelo pequeño o mediano, normalmente nos conviene dejar UMA en Auto. Así Linux conserva más RAM general.
Si usamos el equipo principalmente para ejecutar modelos LLM grandes por Vulkan en la iGPU, puede convenir reservar UMA manualmente. En ese caso, 4 GB, 8 GB o 16 GB pueden tener sentido según la RAM total del equipo y el tamaño de los modelos.
Una regla práctica puede ser esta:
Uso general del equipo: UMA en Auto LLMs pequeños o medianos: UMA en Auto o 4 GB LLMs grandes por Vulkan: probar 8 GB o 16 GB si la BIOS lo permite Equipo con poca RAM total: evitar reservar demasiada UMA Equipo con 64 GB o más: reservar 8 GB o 16 GB puede tener sentido si priorizamos Vulkan
El punto importante es que no estamos creando memoria nueva. Estamos moviendo la frontera entre memoria general del sistema y memoria que el driver gráfico puede presentar de forma más favorable hacia Vulkan.
Cómo medir antes y después de tocar la BIOS
Antes de cambiar nada, deberíamos guardar tres datos: la RAM que ve Linux, la memoria que ve Vulkan y la VRAM/GTT que anuncia AMDGPU. Primero miramos la RAM del sistema:
free -h
Después miramos lo que ve Vulkan desde llama.cpp:
./llama-cli --list-devices
Y después miramos lo que anuncia AMDGPU en el arranque:
sudo dmesg | grep -Ei 'amdgpu.*(VRAM|GTT|memory|TTM|gart)'
Con esos tres comandos podemos construir una tabla real de nuestro equipo.
free -h ./llama-cli --list-devices sudo dmesg | grep -Ei 'amdgpu.*(VRAM|GTT|memory|TTM|gart)'
Después entramos en la BIOS, cambiamos UMA Frame Buffer Size, arrancamos otra vez y repetimos los mismos comandos. Por ejemplo, podríamos probar:
Auto 2 GB 4 GB 8 GB 16 GB
Y anotar los resultados así:
Ajuste BIOS | RAM Linux | VRAM AMDGPU | GTT AMDGPU | Vulkan total Auto | 60 GiB | 2048 MiB | 30968 MiB | 33016 MiB 4 GB | ... | ... | ... | ... 8 GB | ... | ... | ... | ... 16 GB | ... | ... | ... | ...
Lo importante es no basarnos en teoría. Debemos medir nuestro caso concreto.
El parámetro amdgpu.gttsize
Además de la BIOS, existe el parámetro de kernel amdgpu.gttsize, que permite indicar un tamaño para el dominio GTT en MiB. Se puede probar añadiéndolo temporalmente en GRUB o de forma permanente en la línea de comandos del kernel. Por ejemplo, para intentar usar 48 GiB de GTT:
amdgpu.gttsize=49152
En Debian podríamos editar:
sudo nano /etc/default/grub
Y añadir el parámetro en la línea correspondiente:
GRUB_CMDLINE_LINUX_DEFAULT="quiet amdgpu.gttsize=49152"
Luego actualizaríamos GRUB y reiniciaríamos:
sudo update-grub sudo reboot
Después del reinicio, volveríamos a comprobar:
sudo dmesg | grep -Ei 'amdgpu.*(VRAM|GTT|memory|TTM|gart)' ./llama-cli --list-devices
Hay que tener cuidado con este parámetro. Según el kernel, la versión del driver y la plataforma, puede servir para modificar el tamaño de GTT o puede quedar limitado por TTM, el firmware o el propio driver. No debemos asumir que siempre podremos aumentar GTT por encima de lo que el sistema calcula automáticamente. En muchos equipos, el ajuste más fiable y visible es el de UMA en BIOS. El parámetro amdgpu.gttsize puede ser interesante para pruebas avanzadas, pero no sustituye siempre al ajuste de BIOS.
Cómo afecta esto a modelos GGUF concretos
Supongamos que tenemos un modelo GGUF grande, por ejemplo un modelo de 26B parámetros en Q8_0. Un modelo así puede ocupar una cantidad importante de memoria, especialmente si queremos descargar muchas capas a Vulkan.
Primero miramos las capas:
python3 llama.cpp/gguf-py/gguf/scripts/gguf_dump.py --no-tensors /home/usuario/IA/Modelos/modelo.gguf | grep -E "block_count|general.name|general.architecture"
Si vemos algo así:
general.name = 'Google Gemma 4 26B A4B It' gemma4.block_count = 30
Entonces sabemos que el máximo útil para ese modelo será 30 capas. Entonces, para CPU puro:
./llama-cli \ --device none \ -m /home/usuario/IA/Modelos/modelo.gguf \ -p "Hola" \ -ngl 0 \ -t "$(nproc)"
Para Vulkan con todas las capas:
./llama-cli \ --device Vulkan0 \ -m /home/usuario/IA/Modelos/modelo.gguf \ -p "Hola" \ -ngl 30 \ -t "$(nproc)"
Si con UMA en Auto Vulkan ve 32 GiB y el modelo carga, no necesitamos tocar nada. Si no carga, o si queremos probar un contexto mayor, podríamos entrar en BIOS, subir UMA a 8 GB o 16 GB, arrancar de nuevo y repetir la prueba. Si después del cambio Vulkan ve más memoria, el mismo comando podría funcionar donde antes fallaba.
Cómo hacer benchmark del valor -ngl
No siempre el máximo -ngl da el mejor rendimiento. A veces descargar todas las capas a una iGPU con memoria compartida puede ser más lento que usar una combinación parcial de GPU y CPU. La única forma seria de decidirlo es medir. Podemos usar llama-bench:
./llama-bench \ -m /home/usuario/IA/Modelos/modelo.gguf \ -ngl 0,5,10,15,20,25,30 \ -p 512 \ -n 128 \ -t "$(nproc)"
Esto prueba varios niveles de descarga a GPU:
-ngl 0 -> CPU puro -ngl 5 -> 5 capas en Vulkan -ngl 10 -> 10 capas en Vulkan -ngl 15 -> 15 capas en Vulkan -ngl 20 -> 20 capas en Vulkan -ngl 25 -> 25 capas en Vulkan -ngl 30 -> 30 capas en Vulkan
… después elegimos el valor que dé más tokens por segundo sin errores de memoria y sin hacer que el sistema empiece a paginar.
Para vigilar la memoria mientras probamos, podemos usar:
watch -n 1 free -h
También podemos observar la memoria que reporta AMDGPU mediante sysfs en los sistemas donde esas rutas estén disponibles:
find /sys/class/drm -name 'mem_info_*' 2>/dev/null
… y leer valores como:
cat /sys/class/drm/card*/device/mem_info_vram_total 2>/dev/null cat /sys/class/drm/card*/device/mem_info_vram_used 2>/dev/null cat /sys/class/drm/card*/device/mem_info_gtt_total 2>/dev/null cat /sys/class/drm/card*/device/mem_info_gtt_used 2>/dev/null
Si hay varias tarjetas o varios dispositivos DRM, debemos identificar cuál corresponde a la iGPU AMD.
Ejemplo completo con 64 GB de RAM
Supongamos un equipo con 64 GB de RAM física y una iGPU AMD compatible con Vulkan. Con UMA en Auto, la BIOS decide reservar 2 GB porque el equipo tiene más de 32 GB de RAM:
BIOS UMA: Auto UMA real: 2 GiB Linux ve: ~60 GiB GTT: ~30 GiB Vulkan ve: ~32 GiB
Con UMA manual en 4 GB:
BIOS UMA: 4 GiB UMA real: 4 GiB Linux ve: ~58 GiB GTT: ~29 GiB Vulkan ve: ~33 GiB
Con UMA manual en 8 GB:
BIOS UMA: 8 GiB UMA real: 8 GiB Linux ve: ~54 GiB GTT: ~27 GiB Vulkan ve: ~35 GiB
Con UMA manual en 16 GB:
BIOS UMA: 16 GiB UMA real: 16 GiB Linux ve: ~48 GiB GTT: ~24 GiB Vulkan ve: ~40 GiB
La paradoja aparente es que Linux ve menos RAM, pero Vulkan puede ver más memoria total. Esto ocurre porque la parte UMA aumenta más de lo que baja la parte GTT. Si pasamos de 2 GB UMA a 16 GB UMA, sumamos 14 GB a la parte VRAM. Aunque GTT baje aproximadamente de 30 GB a 24 GB, solo perdemos unos 6 GB de GTT. El resultado final es que Vulkan pasa de unos 32 GB a unos 40 GB.
Auto: 2 + 30 = 32 16 GB manual: 16 + 24 = 40
Eso explica por qué puede tener sentido reservar más UMA si nuestro objetivo principal es cargar modelos grandes por Vulkan.
Qué pasa si tenemos menos RAM
En equipos con poca RAM, reservar demasiada UMA puede ser mala idea. Si tenemos 16 GB de RAM física y reservamos 8 GB para la iGPU, Linux puede quedarse con muy poca RAM general para el sistema, el modelo, la caché, el contexto y otros procesos. Aunque Vulkan vea más memoria, el sistema puede quedar desequilibrado.
Un ejemplo aproximado:
RAM física: 16 GiB UMA manual: 8 GiB Linux ve: ~8 GiB GTT aproximada: ~4 GiB Vulkan visible: ~12 GiB
En ese caso, quizá Vulkan vea 12 GiB, pero Linux queda demasiado justo. Para un equipo de 16 GB, normalmente tendría más sentido probar 1 GB, 2 GB o 4 GB de UMA, no 8 GB o 16 GB. Con 32 GB de RAM física, reservar 4 GB u 8 GB puede ser razonable según el uso. Con 64 GB o más, reservar 8 GB o 16 GB puede ser una estrategia válida si queremos priorizar LLMs por Vulkan.
Qué debemos mirar antes de decidir
Antes de elegir Auto, 4 GB, 8 GB o 16 GB, nos conviene responder estas preguntas:
- ¿Cuánta RAM física tiene el equipo?
- ¿Cuánta RAM ve Linux después de cada cambio?
- ¿Cuánta VRAM/UMA reporta AMDGPU?
- ¿Cuánta GTT reporta AMDGPU?
- ¿Cuánta memoria total ve Vulkan?
- ¿Qué modelo LLM queremos cargar?
- ¿Cuántas capas tiene ese modelo?
- ¿Con qué cuantización está guardado el modelo?
- ¿Qué tamaño de contexto queremos usar?
Y, muy importante, es ver si el sistema empieza a usar swap durante la inferencia. Por ello, la decisión correcta no es siempre “más UMA”. La decisión correcta es la que permite cargar el modelo que queremos sin dejar el sistema sin RAM y con el mejor rendimiento real.
Qué no debemos confundir
- No debemos confundir la memoria que Vulkan ve con VRAM física real. En una gráfica integrada AMD, gran parte de esa memoria puede ser RAM compartida.
- No debemos asumir que Auto siempre es mejor. Auto puede ser lo mejor para uso general, pero no necesariamente para exponer el máximo presupuesto posible hacia Vulkan.
- No debemos asumir que reservar 16 GB en BIOS siempre mejora el rendimiento. Puede permitir cargar modelos más grandes o contextos mayores, pero también reduce la RAM general disponible para Linux.
- No debemos asumir que el valor más alto de -ngl siempre es el más rápido. En una iGPU con memoria compartida, a veces una combinación parcial de CPU y GPU puede rendir mejor que descargar todo a Vulkan.
- No debemos ignorar la swap. Si al cargar un modelo grande el sistema empieza a paginar, el rendimiento puede caer en picado aunque Vulkan haya aceptado cargar el modelo.
Los hacks de hacks4geeks son minitutoriales rápidos pensados para geeks con conocimiento informático avanzado. Si no entiendes o no consigues ejecutar un hack de esta web considera suscribirte a Premium para solicitar asistencia sobre el mismo.