Configuração para rodar o modelo MoE de 744B sem interrupções em MacBooks de 32GB de RAM e workstations
Ao executar localmente um modelo MoE de 744B como o GLM-5.2 em uma workstation comum ou MacBook, o gargalo surge primeiro na largura de banda de armazenamento e nas políticas de memória virtual do SO, e não na capacidade de VRAM. O colibrì do JustVugg, um motor de inferência leve baseado em C, fixa na RAM apenas a camada residente de 17B (9,9 GiB com base em int4) e transmite em tempo real, por token, a partir de um SSD externo, os pesos dos especialistas de roteamento divididos em 21.504 partes (~370 GB).
O problema é que, se você o executar com as configurações padrão do SO, ocorrerá swap thrashing ou o OOM Killer do kernel encerrará o processo à força. É necessário ajustar manualmente desde as flags de compilação e as filas de E/S NVMe até os parâmetros do kernel para que a geração de tokens não pare.
Flags de compilação por arquitetura e configuração de conjuntos de instruções vetoriais
O motor de computação principal do colibrì é um único arquivo C11 com cerca de 1.300 linhas, sem dependências externas. O rendimento do cálculo de multiplicação de matrizes varia dependendo de quais instruções vetoriais SIMD o compilador utiliza. Em vez da compilação padrão -O2, você deve especificar diretamente as instruções de extensão de operações inteiras da CPU de destino.
Construção do ambiente de compilação e compilação
Para o ambiente x86_64 Linux, ative o AVX-512 VNNI no GCC 12 ou superior, e no Apple Silicon, conecte o NEON DotProduct via Clang.
Instale primeiro as ferramentas de build nas famílias Ubuntu e Debian.
`bash
sudo apt-get install gcc-12 libomp-dev
`
No ambiente macOS, obtenha o tempo de execução OpenMP com o Homebrew.
`bash
brew install libomp
`
Compile para a máquina x86_64 Linux visando as instruções 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
`
No ambiente Apple Silicon (série M), compile vinculando diretamente o caminho da 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
`
Se surgirem avisos de compilação relacionados a estruturas, adicione a flag -std=c11 para resolvê-los.
| Plataforma |
Compilador |
Flag de instrução vetorial |
Paralelização por threads |
| x86_64 (Linux) |
GCC 12+ |
-mavx512vnni -mavx512bw |
-fopenmp |
| ARM64 (macOS) |
Apple Clang |
-march=armv8.4-a+dotprod |
-Xpreprocessor -fopenmp |
| x86 antigo |
GCC / Clang |
-mavx2 -mfma |
-fopenmp |
Benchmark de SSD externo e expansão da fila de E/S assíncrona
A latência de decodificação do colibrì é determinada pela velocidade de leitura aleatória de blocos de 1 MB do disco. Os arquivos de parâmetros dos especialistas de roteamento do GLM-5.2 são divididos em tamanhos de cerca de 19 MB cada, e a cada geração de token, a thread de E/S assíncrona lê os blocos necessários do SSD.
Medição da largura de banda de leitura de armazenamento via fio
Instale o fio com o gerenciador de pacotes.
`bash
Linux
sudo apt-get install fio
macOS
brew install fio
`
Meça a largura de banda de leitura aleatória com o tamanho de bloco de 1 MB e a condição de E/S Direta.
`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
`
As interfaces Thunderbolt 4 ou USB4 (40 Gbps) mantêm uma largura de banda real de 2.800 a 3.200 MB/s, alcançando uma velocidade de decodificação de 0,08 a 0,10 tok/s. Por outro lado, se você conectar a unidade externa a uma porta USB 3.2 Gen2 (10 Gbps) com largura de banda limitada a 900-1.050 MB/s, levará mais de 80 minutos para extrair 100 tokens.
`
+-------------------------------------------------------------------------+
| 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 | |
| +--------------------------+ +-------------------------------------+ |
+-------------------------------------------------------------------------+
`
Ao aumentar a profundidade da fila de E/S (iodepth) do pré-carregamento assíncrono (Async Expert Readahead) para o nível de 32 a 64, você pode aproveitar todos os canais paralelos do controlador NVMe e reduzir a latência de espera do disco.
Configurações de memória virtual do kernel e prevenção de OOM
Em um sistema com 32 GB de RAM, se os pesos residentes de 9.9 GiB, o cache KV e o cache de páginas do sistema de arquivos subirem simultaneamente, ocorrerá contenção de memória no kernel. Nas configurações padrão do Linux, a chamada mmap para o tamanho de 370 GB tem a alocação de espaço de endereço virtual bloqueada ou o OOM Killer encerra o motor.
Aplicação de parâmetros do kernel Linux
Crie o arquivo /etc/sysctl.d/99-colibri-memory.conf e insira o conteúdo abaixo.
`ini
Suprimir a liberação de memória residente para o swap em disco
vm.swappiness = 1
Permitir overcommit de memória virtual
vm.overcommit_memory = 1
Garantir espaço de memória de buffer de emergência (2GB)
vm.min_free_kbytes = 2097152
Aumentar a taxa de preservação de cache de arquivos em disco
vm.vfs_cache_pressure = 50
Expandir o limite do número de mapeamentos mmap
vm.max_map_count = 524288
`
Aplique os parâmetros imediatamente após salvar.
`bash
sudo sysctl --system
`
Remova o limite de bloqueio de memória na sessão do shell para permitir chamadas mlock() para a camada residente.
`bash
ulimit -l unlimited
`
| Parâmetro |
Padrão |
Recomendado |
Objetivo da configuração |
| vm.swappiness |
60 |
1 |
Prevenir que os pesos de 9.9 GiB fixados na RAM sejam empurrados para a área de swap |
| vm.overcommit_memory |
0 |
1 |
Evitar recusa de memória do kernel ao mapear mmap de 370 GB |
| vm.vfs_cache_pressure |
100 |
50 |
Aumentar a vida útil do cache de arquivos para reutilizar pesos de especialistas chamados repetidamente |
| vm.max_map_count |
65530 |
524288 |
Remover o limite de mapeamento de 21.504 arquivos de parâmetros |
| ulimit -l |
64 (KB) |
unlimited |
Conceder permissão de fixação de memória física (mlock) para a camada densa |
No ambiente macOS, quando a memória livre se esgota, o dynamic_pager aplica compressão de memória em segundo plano, consumindo a porcentagem de uso da CPU. Antes de executar, você deve encerrar aplicativos com alto consumo para evitar a entrada no motor de compressão.
Fixação de núcleos de CPU e configuração do script de execução
A decodificação MoE consome intensamente o cache da CPU e o barramento de memória. Se você selecionar incorretamente o nó NUMA em uma workstation com múltiplos sockets, a velocidade de geração de tokens cai pela metade devido ao gargalo no barramento de interconexão.
Criação do script de execução integrado
Verifique primeiro o número do nó da CPU física conectado diretamente ao controlador NVMe externo usando numactl --hardware. Em seguida, escreva um script de shell para evitar a alocação duplicada de Hyper-Threading e fixar as threads apenas nos núcleos físicos.
`bash
#!/usr/bin/env bash
set -euo pipefail
1. Configuração de memória virtual do kernel
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. Remoção do limite de bloqueio de memória
ulimit -l unlimited
3. Vinculação de núcleos físicos para threads OpenMP
export OMP_NUM_THREADS=16
export OMP_PROC_BIND=TRUE
export OMP_PLACES=cores
4. Execução vinculada ao nó 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}"
`
Em MacBooks com Apple Silicon, você deve impedir que o daemon QoS do macOS desloque as threads de trabalho para os núcleos de eficiência (E-Core). Execute aumentando forçadamente a prioridade.
`bash
sudo nice -n -20 taskpolicy -c default ./colibri_engine --model /Volumes/SSD/glm52_i4
`
Ao concluir estas quatro configurações, você poderá carregar de forma estável o modelo MoE de 744B em uma workstation de 32 GB de RAM ou MacBook usando o método de streaming em disco local, sem conflitos por OOM.