Configuración para ejecutar sin interrupciones el modelo MoE 744B en un Mac de 32GB de RAM y estaciones de trabajo
Al ejecutar localmente un modelo MoE de 744B como GLM-5.2 en una estación de trabajo común o en un MacBook, el cuello de botella surge primero en el ancho de banda del almacenamiento y en las políticas de memoria virtual del SO, antes que en la capacidad de VRAM. colibrì, un motor de inferencia ligero basado en el lenguaje C, fija en la RAM únicamente las capas residentes de 17B (9.9 GiB basados en int4) y transmite en tiempo real por token, desde un SSD externo, los pesos de los expertos de enrutamiento divididos en 21,504 partes (~370 GB).
El problema es que, si se ejecuta con la configuración predeterminada del SO, se produce thrashing de paginación (swap thrashing) o el OOM Killer del núcleo finaliza el proceso de forma forzosa. Es necesario ajustar manualmente desde las banderas de compilación y las colas de E/S NVMe hasta los parámetros del núcleo para que la generación de tokens no se detenga.
Banderas de compilación por arquitectura y configuración de conjuntos de instrucciones vectoriales
El motor de cálculo central de colibrì es un único archivo C11 de unas 1,300 líneas sin dependencias externas. El rendimiento del procesamiento de multiplicaciones de matrices varía según las instrucciones vectoriales SIMD que utilice el compilador. En lugar de la compilación predeterminada -O2, es necesario especificar directamente las instrucciones de extensión de enteros de la CPU de destino.
Construcción del entorno de compilación y compilación
En entornos Linux x86_64, active AVX-512 VNNI con GCC 12 o superior; en Apple Silicon, conecte NEON DotProduct a través de Clang.
Para las familias Ubuntu y Debian, instale primero las herramientas de compilación.
`bash
sudo apt-get install gcc-12 libomp-dev
`
En el entorno macOS, obtenga el tiempo de ejecución OpenMP con Homebrew.
`bash
brew install libomp
`
En máquinas Linux x86_64, compile apuntando a las instrucciones AVX-512 VNNI.
`bash
gcc -O3 -march=native -mtune=native
-mavx512f -mavx512bw -mavx512vnni -mavx512dq
-fopenmp -funroll-loops -ffast-math
-o colibri_engine colibri_glm52.c -lm
`
En entornos Apple Silicon (serie M), compile enlazando directamente la ruta de la biblioteca.
`bash
clang -O3 -mcpu=native
-march=armv8.4-a+dotprod+fp16
-Xpreprocessor -fopenmp
-I/opt/homebrew/opt/libomp/include
-L/opt/homebrew/opt/libomp/lib -lomp
-o colibri_engine colibri_glm52.c -lm
`
Si aparecen advertencias de compilación relacionadas con estructuras, resuélvalas añadiendo la bandera -std=c11.
| Plataforma |
Compilador |
Banderas de instrucciones vectoriales |
Paralelización de hilos |
| x86_64 (Linux) |
GCC 12+ |
-mavx512vnni -mavx512bw |
-fopenmp |
| ARM64 (macOS) |
Apple Clang |
-march=armv8.4-a+dotprod |
-Xpreprocessor -fopenmp |
| x86 antiguo |
GCC / Clang |
-mavx2 -mfma |
-fopenmp |
Pruebas de rendimiento de SSD externo y ampliación de la cola de E/S asíncrona
La latencia de decodificación de colibrì está determinada por la velocidad de lectura aleatoria del disco en bloques de 1 MB. Los archivos de parámetros de los expertos de enrutamiento de GLM-5.2 están divididos en tamaños de aproximadamente 19 MB cada uno, por lo que cada vez que se genera un token, el hilo de E/S asíncrono lee los fragmentos necesarios desde el SSD.
Medición del ancho de banda de lectura de almacenamiento mediante fio
Instale fio con el administrador de paquetes.
`bash
Linux
sudo apt-get install fio
macOS
brew install fio
`
Mida el ancho de banda de lectura aleatoria con un tamaño de bloque de 1 MB y condiciones de E/S directa (Direct I/O).
`bash
fio --name=colibri_stream_bench
--filename=/Volumes/ExternalSSD/glm52_test.tmp
--size=10G
--rw=randread
--bs=1m
--iodepth=32
--numjobs=4
--ioengine=posixaio
--direct=1
--group_reporting
--runtime=60
`
Las interfaces Thunderbolt 4 o USB4 (40 Gbps) mantienen un ancho de banda real de 2,800 a 3,200 MB/s, ofreciendo una velocidad de decodificación de 0.08 a 0.10 tok/s. Por el contrario, si se conecta la unidad externa a un puerto USB 3.2 Gen2 (10 Gbps) cuyo ancho de banda se limita a 900-1,050 MB/s, extraer 100 tokens tomará más de 80 minutos.
`
+-------------------------------------------------------------------------+
| colibrì C Inference Engine |
| +--------------------------+ +-------------------------------------+ |
| | Dense Weights (9.9 GiB) | | Multi-Token Prediction (MTP) Head |
| | Resident in RAM (mlock) | | int8 Quantized |
| +------------+-------------+ +------------------+------------------+ |
+---------------+-----------------------------------+---------------------+
| |
v v
+-------------------------------------------------------------------------+
| Memory & Storage I/O Layer |
| +--------------------------+ +-------------------------------------+ |
| | Async Expert Readahead | | 21,504 Routed Experts (370 GB) |
| | I/O Queue Depth 3264 | | Streamed from NVMe SSD via mmap |
| +--------------------------+ +-------------------------------------+ |
+-------------------------------------------------------------------------+
`
Si se aumenta la profundidad de la cola de E/S (iodepth) de la prelectura asíncrona de expertos (Async Expert Readahead) a un nivel de 32 a 64, es posible aprovechar por completo los canales paralelos del controlador NVMe para reducir la latencia de espera del disco.
Configuración de memoria virtual del núcleo y prevención de OOM
En un sistema con 32 GB de RAM, si los pesos residentes de 9.9 GiB, el búfer KV y la caché de páginas del sistema de archivos se cargan simultáneamente, se produce competencia de memoria en el núcleo. En la configuración predeterminada de Linux, al realizar una llamada mmap de 370 GB, se bloquea la asignación del espacio de direcciones virtuales o el OOM Killer finaliza el motor.
Aplicación de parámetros del núcleo de Linux
Cree el archivo /etc/sysctl.d/99-colibri-memory.conf e introduzca el siguiente contenido:
`ini
Supresión de la expulsión de memoria residente al intercambio de disco
vm.swappiness = 1
Permitir sobreasignación de memoria virtual
vm.overcommit_memory = 1
Garantizar espacio de memoria del búfer de emergencia (2GB)
vm.min_free_kbytes = 2097152
Aumento de la tasa de retención de la caché de archivos de disco
vm.vfs_cache_pressure = 50
Ampliación del límite de conteo de mapeos mmap
vm.max_map_count = 524288
`
Aplique los parámetros inmediatamente después de guardar.
`bash
sudo sysctl --system
`
Elimine el límite de bloqueo de memoria en la sesión de shell para permitir la llamada mlock() de las capas residentes.
`bash
ulimit -l unlimited
`
| Parámetro |
Valor predeterminado |
Valor recomendado |
Propósito de la configuración |
| vm.swappiness |
60 |
1 |
Evitar que los pesos de 9.9 GiB fijados en la RAM sean desplazados al área de intercambio |
| vm.overcommit_memory |
0 |
1 |
Prevenir el rechazo de memoria del núcleo al mapear mmap de 370 GB |
| vm.vfs_cache_pressure |
100 |
50 |
Extender la vida útil de la caché de archivos para reutilizar los pesos de expertos llamados repetidamente |
| vm.max_map_count |
65530 |
524288 |
Levantar el límite de mapeo de los 21,504 archivos de parámetros |
| ulimit -l |
64 (KB) |
unlimited |
Otorgar permisos de fijación en memoria física (mlock) para las capas densas |
En el entorno macOS, si se agota la memoria libre, dynamic_pager aplica compresión de memoria en segundo plano, lo que consume uso de CPU. Debe cerrar las aplicaciones con alto consumo antes de ejecutar para evitar la entrada del motor de compresión.
Fijación de núcleos de CPU y configuración del script de ejecución
La decodificación MoE satura constantemente la caché de la CPU y el bus de memoria. Si se selecciona incorrectamente el nodo NUMA en estaciones de trabajo de múltiples sockets, la velocidad de generación de tokens se reduce a la mitad debido al cuello de botella en el bus de interconexión.
Creación del script de ejecución integrado
Verifique primero con numactl --hardware el número del nodo de CPU física conectado directamente al controlador NVMe externo. A continuación, escriba un script de shell que evite la asignación duplicada por hiperhilos (Hyper-Threading) y fije los hilos exclusivamente a los núcleos físicos.
`bash
#!/usr/bin/env bash
set -euo pipefail
1. Configuración de memoria virtual del núcleo
sudo sysctl -w vm.swappiness=1 > /dev/null
sudo sysctl -w vm.overcommit_memory=1 > /dev/null
sudo sysctl -w vm.max_map_count=524288 > /dev/null
sudo sysctl -w vm.vfs_cache_pressure=50 > /dev/null
sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null
2. Liberación del límite de bloqueo de memoria
ulimit -l unlimited
3. Enlace de hilos OpenMP a núcleos físicos
export OMP_NUM_THREADS=16
export OMP_PROC_BIND=TRUE
export OMP_PLACES=cores
4. Ejecución enlazada al nodo NUMA 0
ENGINE_BIN="./colibri_engine"
MODEL_PATH="/mnt/nvme_ext/GLM-5.2-colibri-int4-g64-with-int8-mtp"
exec numactl --physcpubind=0-15 --membind=0
"${ENGINE_BIN}"
--model "${MODEL_PATH}"
--threads "${OMP_NUM_THREADS}"
`
En MacBooks con Apple Silicon, debe evitarse que el demonio QoS de macOS mueva los hilos de trabajo hacia los núcleos de eficiencia (E-Core). Ejecute elevando la prioridad de forma forzosa.
`bash
sudo nice -n -20 taskpolicy -c default ./colibri_engine --model /Volumes/SSD/glm52_i4
`
Al finalizar estas cuatro configuraciones, podrá cargar de manera estable el modelo MoE de 744B mediante el método de transmisión en disco local sin sufrir colapsos por OOM, incluso en una estación de trabajo o MacBook con 32 GB de RAM.