El zip de Linux no trae CUDA. Ollama vs llama.cpp en 12 GB
Iba a medir Ollama contra llama.cpp. El zip de Linux no trae CUDA. El que ya tenía en PATH era Vulkan.
La pregunta no era cuál motor es "mejor"
Tenía un Qwen MoE 35B-A3B en Q4 (~20 GB). Ollama 0.32.15 ya lo servía. La tarjeta es una RTX 3060 de 12 GB, CPU i9-13900K, 32 GB de RAM. En papel el MoE cabe híbrido: activas ~3B, el resto de expertos puede vivir en RAM.
La duda era concreta: si compilaba llama.cpp con CUDA, ¿los knobs de la comunidad (flash attn, KV cuantizado, experts en CPU) se notaban contra el default de Ollama?
Misma GGUF. Mismos 16 threads. Un motor a la vez. Nada de pelearse la VRAM.
En Linux no hay tarball CUDA
Parlor ya tenía un llama-server en PATH. Es Vulkan. Los assets oficiales de llama.cpp para Linux son CPU, Vulkan, SYCL, OpenVINO. CUDA empacado aparece en Windows.
Si comparas Ollama CUDA contra llama.cpp Vulkan, no estás midiendo el mismo backend.
Compilé v0.3.0 (commit c1d0e7a) con CUDA 12.0, g++-12, -DCMAKE_CUDA_ARCHITECTURES=86 y flash attention con todos los quants. Prefijo: un directorio aparte. El wrapper de Parlor sigue apuntando al build Vulkan.
llama-cli-cuda --list-devices
# CUDA0: NVIDIA GeForce RTX 3060 (11904 MiB, ...)
llama-server --list-devices
# Vulkan0: NVIDIA GeForce RTX 3060
Dos binarios. Mismo GPU. Backends distintos.
-j 32 tumba nvcc
El primer build fue cmake --build ... -j 32. nvcc se cayó en un instance de fattn-mma-f16. Con 32 jobs y GGML_CUDA_FA_ALL_QUANTS, el compilador se queda sin RAM.
-j 8 terminó bien. cmake --install falló después porque no habíamos compilado los tests. Copié llama-cli, llama-server, llama-bench y las .so versionadas al prefijo. Un wrapper con LD_LIBRARY_PATH por cada uno (llama-cli-cuda, etc.), el mismo truco que ya usaba Parlor.
-ngl 99 pide 20 GB y se muere
El comando que ves en todos lados:
llama-cli-cuda -m modelo.gguf -ngl 99
Log real:
ggml_backend_cuda_buffer_type_alloc_buffer: allocating 19902.90 MiB on device 0: cudaMalloc failed: out of memory
alloc_tensor_range: failed to allocate CUDA0 buffer of size 20869704192
llama_model_load: error loading model: unable to allocate CUDA0 buffer
-ngl 99 (o -1 / all) no significa "usa la GPU lo que puedas". Significa: intentá offload de todos los layers. En un denso de 7B entra. En un MoE de 20 GB, no.
Ollama, en cambio, llena solo. En esta corrida se quedó en 10865 MiB y sirvió.
Tres formas de partir el modelo
1. Experts en CPU. -cmoe / -ncmoe 99 y -ngl 99 para lo que no es MoE.
llama-bench, 3 reps, flash attn on:
pp512 266 ± 66 tok/s
tg128 41.4 ± 0.3 tok/s
VRAM ~2350 MiB
La GPU queda casi vacía. Generación usable, no es el techo.
2. Auto-fit. -fitt 1024: deja ~1 GB de margen y llama.cpp acomoda el resto.
pp512 609 ± 11 tok/s
tg128 56.4 ± 0.6 tok/s
El prompt se dispara. La generación se acerca a Ollama.
3. Ollama default. HTTP /api/generate, raw=true, think=false, num_thread=16.
prompt 1120 tok (1ª pasada) 67 tok/s
gen 118 tok (3 corridas) 61.0 / 61.3 / 61.4 tok/s
VRAM 10865 MiB
Las pasadas 2 y 3 del prompt marcaron ~16000 tok/s. Eso es cache del prefijo. Si promedias, inventas un ganador.
Ollama gana un poco en generación. llama.cpp, con auto-fit, aplasta el prefill. El default de Ollama ya hace el trabajo sucio de caber en 12 GB. llama.cpp te lo deja a ti, y si copias
-ngl 99ni siquiera arranca.
No es una comparativa de laboratorio puro. llama-bench usa pp512/tg128 sintético. Ollama recibió un prompt de ~1120 tokens por HTTP. El orden de magnitud alcanza para decidir cómo lo vas a servir.
Cómo se usa, de verdad
Ollama ya escucha en local:
http://127.0.0.1:11434
ollama run qwen3.6-35b-a3b
llama.cpp CUDA no queda como daemon al instalarlo. Hay que levantar el server:
llama-server-cuda \
-m /ruta/al/modelo.gguf \
--host 127.0.0.1 --port 8080 \
--fit on -fitt 1024 -fa on \
-t 16
UI: http://127.0.0.1:8080
API estilo OpenAI: POST http://127.0.0.1:8080/v1/chat/completions
CLI, sin URL:
llama-cli-cuda -m /ruta/al/modelo.gguf -fitt 1024 -fa on -c 2048 -n 256 -st -p 'hola'
No corras Ollama con el modelo cargado y llama-server-cuda a la vez. Una 3060 no da para los dos.
Tampoco pises el llama-server de Parlor: ese es Vulkan. El de CUDA tiene otro nombre a propósito.
Lo que me quedo
El binario de Linux no te ahorra el toolkit de CUDA si quieres el mismo backend que Ollama.
-ngl 99 en un MoE de 20 GB no es un atajo. Es un malloc de 20 GB.
Si publicas tok/s sin decir offload, VRAM y si el prompt estaba cacheado, el número no sirve.
En esta máquina, para chat, Ollama default está bien y es más simple. llama.cpp entra cuando quiero el prefill rápido o knobs que Ollama no expone. Los dos usan la misma GGUF. El que cambia es quién decide qué tensores se quedan en la 3060.