- O colibrì é um motor puro em C feito para executar o GLM-5.2 744B MoE em máquinas de consumo com cerca de 25 GB de RAM, funcionando sem GPU ao fazer streaming dos experts roteados a partir do disco
- A arquitetura principal mantém a parte densa com cerca de 17B parâmetros residentes na RAM em int4, usando 9,9 GB, enquanto os 21.504 experts roteados ficam em cerca de 370 GB de disco e são lidos quando necessário
- A implementação inclui forward do GLM-5.2, atenção MLA com KV-cache comprimido, router no estilo DeepSeek-V3, speculative decoding com MTP, kernels de quantização int8/int4/int2 e tokenizer BPE em nível de byte
- Em um ambiente com WSL2, 12 núcleos, 25 GB de RAM e NVMe via VHDX, o tempo de carga é de cerca de 30 segundos, o pico de RSS durante o chat fica em torno de 20 GB, e o decode cold chega a 0,05~0,1 tok/s com cerca de 11 GB de leitura de disco por token
- Em sistemas com NVMe mais rápido e mais RAM, cache e pinning são importantes; em medições reais da comunidade, um Apple M5 Max com 128 GB registrou 1,06 tok/s com MTP desligado
O problema que o colibrì quer resolver
- colibrì se apresenta com o slogan “Tiny engine, immense model” e é um motor para executar o modelo GLM-5.2 MoE de 744 bilhões de parâmetros em máquinas de consumo com cerca de 25 GB de RAM
- O runtime é puro C, sem dependências de Python, BLAS ou GPU
- O motor é composto por um único arquivo C
c/glm.c com cerca de 1.300 linhas, além de pequenos headers
- O exemplo de execução é
./coli chat, e a saída de exemplo mostra colibrì v1.0 — GLM-5.2 · 744B MoE · int4 · streaming CPU, com cerca de 32 segundos de preparação e 9,9 GB de memória residente
Como funciona a execução de MoE com streaming a partir do disco
- O modelo Mixture-of-Experts de 744B do GLM-5.2 ativa apenas cerca de 40B parâmetros por token
- Os experts roteados, que mudam a cada token, equivalem a cerca de 11 GB
- A parte densa fica residente na RAM
- inclui attention, shared experts e embeddings
- cerca de 17B parâmetros
- usa cerca de 9,9 GB de RAM em int4
- Os experts roteados ficam armazenados no disco
- 75 camadas MoE × 256 experts, além do head de MTP
- total de 21.504 experts roteados
- cerca de 19 MB por expert em int4
- uso total de disco de cerca de 370 GB
- O carregamento de experts usa cache LRU por camada, hot-store fixado opcionalmente e cache de páginas do sistema operacional
Principais recursos implementados
- O forward do GLM-5.2 foi implementado conforme a estrutura
glm_moe_dsa e validado como token-exact em relação ao oracle do transformers
- teacher-forcing 32/32 em modelo tiny-random
- geração greedy 20/20
- A atenção MLA inclui q/kv-LoRA e partial RoPE intercalado
- o KV-cache comprimido usa 576 floats por token
- 57 vezes menor que os 32.768 floats/token anteriores
- o GLM-5.2 tem 64 heads e não usa GQA
- Foi implementado um router sigmoide no estilo DeepSeek-V3
noaux_tc
routed_scaling_factor
- shared expert
- first-3-dense layers
- O speculative decoding nativo com MTP usa o head de multi-token-prediction do GLM-5.2 (camada 78) para criar draft tokens, que o modelo principal valida com batched forward
- o head de MTP precisa estar em int8
- em int4, a aceitação de drafts cai para 0~4%, e a especulação não funciona
- em int8, a aceitação medida ficou em 39~59%, com 2,2~2,8 tokens/forward
- continua lossless durante sampling por meio de rejection sampling
- com cache cold, drafts verificados podem rotear experts extras, elevando os expert-loads/token de cerca de 660 para cerca de 1100
- nesse caso, pode levar mais tempo até que cache e pin aqueçam, então há adaptive guard e a opção
DRAFT=0
- O true sampling suporta temperature e nucleus
- padrão de 0,7 / 0,90
- as configurações oficiais 1,0 / 0,95 são descritas como amostrando ruído de quantização na cauda em ambiente int4
- O kernel integer-dot usa ativações int8 no estilo Q8_0 e
maddubs AVX2
- o matmul em int8 é 1,4~2,5 vezes mais rápido
- desempenho medido de 119 GFLOP/s
- int4 é 1,8 vez mais rápido em batch
- int4 em linha única foi medido como mais lento, então permanece em f32
- A absorção de pesos MLA evita reconstrução de k/v por token durante o decode
- a query absorve
kv_b, e o contexto é projetado após a attention
- validado com TF 32/32 e generation 20/20 mesmo em ambiente de absorção forçada
- O readahead de experts lê o próximo bloco com
WILLNEED enquanto multiplica um bloco de expert
- Os kernels de quantização suportam int8, int4 empacotado, int2 empacotado, escalas por linha, AVX2 e dequant-on-use
- A atenção esparsa DSA está em andamento
- os pesos do lightning-indexer extraem cerca de 108 GB do repositório FP8
- o forward do indexer está planejado como próximo passo
- até lá, a attention é densa e exata para contexto ≤ 2048 tokens
- Em prefill e verificação de MTP, o Batch-union MoE lê os experts únicos do batch apenas uma vez e os aplica a todas as posições roteadas para aquele expert
- O tokenizer é um tokenizer BPE em nível de byte implementado em C, usando regex de propriedade Unicode no estilo GPT-2 e 320k merges
- A proteção de segurança de RAM redimensiona automaticamente o cache de experts na inicialização com base em
MemAvailable
- considera a projeção de pico de working set, KV, linha de MTP e buffer de reconstrução
- foi projetado para evitar que o OOM-killer do kernel seja acionado
- O conversor offline é fornecido como
c/convert_fp8_to_int4.py
- baixa os shards FP8 do GLM-5.2 um por vez
- faz dequant com block scales 128×128 e depois requantiza para o container do motor
- avança apagando os shards, então não é necessário que o checkpoint FP8 inteiro de 756 GB exista no disco ao mesmo tempo
- a conversão pode ser retomada
Desempenho-base medido e limitações
- O ambiente de desenvolvimento é WSL2, 12 núcleos, 25 GB de RAM, NVMe via VHDX
- As medições são as seguintes
- tamanho do modelo no container int4: cerca de 370 GB
- RAM residente: 9,9 GB
- tempo de carga: cerca de 30 segundos
- pico de RSS durante o chat: cerca de 20 GB, limitado automaticamente
- custo de decode cold: cerca de 11 GB de leituras de disco por token
- teto de disco aleatório no VHDX: cerca de 1 GB/s
- velocidade de decode cold: cerca de 0,05~0,1 tok/s
- especulação com MTP: 2,2~2,8 tok/forward com head int8
- O texto destaca que não se trata de um sistema rápido e explica que cache aquecido, hot experts fixados e MTP reduzem a latência para respostas úteis
- Há dois alertas sobre SSD
- como o streaming do colibrì é read-only, a leitura em si não afeta de forma relevante o desgaste do SSD
- se houver tráfego de swap por falta de RAM, isso gera escrita e pode desgastar a unidade
- um ciclo prolongado de leitura total pode aquecer unidades mais baratas, então é preciso monitorar temperatura e saúde
Download e execução do modelo
- O modelo GLM-5.2 int4 pré-convertido está disponível no Hugging Face
- Ao baixar o modelo pré-convertido, é possível pular a etapa de conversão FP8 → int4
- A execução define
COLI_MODEL como o diretório do modelo
COLI_MODEL=/path/to/GLM-5.2-colibri-int4 ./coli chat
- O quick start executa
./setup.sh no diretório c para verificar gcc/OpenMP, compilar e rodar self-test
- Para converter manualmente, use
./coli convert --model /nvme/glm52_i4
- é necessário um caminho ext4/NVMe com cerca de 400 GB livres
- a conversão exige Python e
torch, safetensors, huggingface_hub, numpy
- o runtime do motor em si continua sendo puro C, e o Python é usado apenas no conversor de uso único
- As opções úteis são as seguintes
--temp T: temperature de sampling, padrão 0,7 e nucleus 0,90; 0 é greedy
--topp 0.7: top-p adaptativo de experts, reduz uso de disco em 30~40%
--ngen N: número máximo de tokens por resposta
AUTOPIN=0: desativa o auto-pin do learning cache
THINK=1: ativa o bloco de raciocínio do GLM-5.2
DRAFT=n: profundidade de draft do MTP
TF=1: validação teacher-forcing
Learning cache e expectativas por hardware
- O learning cache registra os experts roteados em uso real em
.coli_usage ao lado do modelo
- Na inicialização, ele fixa automaticamente os experts mais quentes na RAM disponível
- A descrição do projeto diz que o colibrì fica mais rápido à medida que o uso se acumula
- O ambiente exigido é Linux ou WSL2, gcc com suporte a OpenMP, AVX2, no mínimo 16 GB de RAM e um NVMe local com o modelo int4 de cerca de 370 GB
- recomenda-se caminho ext4
- a orientação é não usar mounts de rede/9p
- A sequência de testes é build e self-test, medição do disco com
iobench, verificação de tok/s, expert hit-rate e RSS no chat, registro do uso de experts seguido de pinning, e execução dos benchmarks de qualidade
- As expectativas por hardware são estimativas, não medições
- máquina de desenvolvimento com WSL2 VHDX, cerca de 1 GB/s, 25 GB de RAM: 0,05~0,1 tok/s cold
- Linux nativo, NVMe PCIe4 com 3~5 GB/s aleatório, 32 GB: 0,5~1 tok/s
- NVMe PCIe5 ou RAID0 com 2× NVMe em 8~12 GB/s, 64 GB, PIN de cerca de 40 GB: 2~4 tok/s
- 128~256 GB de RAM, 12 núcleos, hot experts em cache: 2~4 tok/s
- mesma RAM com 24~32 núcleos ou kernels AVX-512/VNNI: 5~15 tok/s
Benchmarks da comunidade
- Em um ambiente com Intel Core Ultra 7 270K Plus, 24 threads, WSL2, 24 GB de RAM e NVMe VHDX, a configuração padrão registrou 0,07 tok/s
- disk iobench: 1,96 GB/s buffered, 2,74 GB/s O_DIRECT
- expert hit: 3~4%
- RSS: 14,1 GB
- No mesmo ambiente,
--topp 0.7 registrou 0,11 tok/s
- expert hit: 11%
- RSS: 14,7 GB
- a velocidade end-to-end aumentou 1,6 vez
- Em um Apple M5 Max, 18 cores, macOS, 128 GB de memória unificada e SSD interno, foram registrados 1,06 tok/s
- disk iobench: 14,2 GB/s O_DIRECT
- padrão, com MTP desligado
- expert hit: 23%
- RSS: 21,8 GB
- Em máquinas com 24 GB de RAM, o cache de experts é automaticamente limitado a 2 slots por camada, então mesmo com disco 2~2,7 vezes mais rápido que o da máquina de desenvolvimento, o decode permanece em estado cold
- Em máquinas com pouca RAM, o gargalo passa a ser o limite de RAM, e não o disco
- A medição no M5 Max mostra um caso em que o modelo 744B entrega cerca de 1 tok/s em um SSD de laptop, e um disco de 14 GB/s desloca o gargalo de volta para o orçamento de RAM e os kernels
Benchmarks de qualidade e medições pendentes
- O custo em precisão da quantização int4 ainda não foi medido
- O benchmark harness foi implementado, mas em um disco de cerca de 1 GB/s como o da máquina de desenvolvimento, a execução completa leva boa parte de um dia
- Os comandos de benchmark fornecidos executam
hellaswag, arc_challenge, mmlu
cd c
./coli bench
./coli bench hellaswag --limit 200
./coli bench mmlu arc_challenge --ram 100
- A saída mostra a acurácia por tarefa e usa scoring por log-likelihood no estilo EleutherAI-harness
- O texto afirma que as pontuações públicas do GLM-5.2 em precisão total ficam em cerca de 85~95% nessas tarefas
- Se o container int4 ficar a poucos pontos disso, a quantização estará validada; caso contrário, isso justifica investir em quantização mista ou grouped-scale
Estrutura do repositório e licença
- A composição principal dos arquivos é a seguinte
c/glm.c: motor com forward do GLM-5.2, streaming MoE, MTP e modo serve
c/st.h: leitor de safetensors baseado em pread e fadvise, sem mmap
c/tok.h: tokenizer BPE em nível de byte em C
c/coli: CLI para chat, run, bench, convert e info
c/iobench.c: microbenchmark paralelo de disco como percebido pelo motor
c/convert_fp8_to_int4.py: conversor seguro em disco de FP8 → int4
c/make_glm_oracle.py: gerador de oracle tiny-random para validação
c/olmoe.c: motor Stage-A e primeiro alvo de validação
- O nome colibrì vem do beija-flor, que consegue pairar com pouco peso e visitar muitas flores por dia
- A licença do projeto é Apache 2.0, e os pesos do GLM-5.2 foram publicados sob MIT pela Z.ai
1 comentários
Comentários do Hacker News
O que mais me deixa curioso é se, no uso real, isso chega a um nível medido em tokens/segundo ou se fica mais perto de 1 token por minuto
Mesmo quando um LLM local é lento, na faixa de 1 token/segundo, ainda achei bem útil deixá-lo trabalhando em um projeto durante a noite e conferir o resultado 6 a 8 horas depois
Por outro lado, os 0,05 a 0,1 token/segundo do hardware mínimo citado no link parecem ter pouquíssimo uso prático
Ainda assim, o conceito como um todo é excelente, e espero ver mais tentativas de rodar localmente modelos na faixa de 350B a 900B em hardware que pessoas comuns consigam pagar, mesmo que seja só a 1 token/segundo
Gosto da direção de “tenho um SSD NVMe com leitura rápida e um modelo gigantesco no disco local, então em vez de carregar tudo, vou ler 11 GB por token quando precisar”
Agora acho que, no mais tardar até 2028, estarei rodando isso localmente
Quero manter o orçamento abaixo de 10 mil dólares e rodar um modelo comparável aos melhores de hoje
Antes de gastar dinheiro com um Mac Studio ou uma GPU, decidi uma forma de validar essa ideia com o hardware que já tenho, e acho que consigo montar uma prova de conceito ainda esta semana
Não é uma ideia nova; só preciso de tempo para fazê-la funcionar do jeito que se encaixa no meu fluxo de trabalho
Continuo tentando melhorar ainda mais
1 token por minuto é um número pequeno demais até comparado ao limite de uso gratuito
Estou fazendo algo parecido voltado para macOS em Apple Silicon
A direção é usar Unsloth split GGUF, residência parcial comprimida na memória unificada, kernels Metal nativos e KV nativo comprimido só em RAM
Parece fazer mais sentido em 128 GB do que nos meus 64 GB, e pretendo colocar no GitHub quando estiver pronto
Também experimentei uma estratégia parecida para geração de imagem/vídeo com https://github.com/cretz/thinfer
Tem muito trabalho entrando na branch
videoEu continuava precisando de um motor de inferência que pudesse carregar e descarregar pesos em LRU conforme necessário, então acabei fazendo, no vibe coding, uma ferramenta que recebe
--vram-budgete fica dentro desse limiteFicou claro que o custo de mover bytes mapeados por mmap para dentro e para fora da VRAM é bem barato em comparação com o cálculo, e que, com uma mistura de pipelining e buffer duplo, quase sempre o gargalo acaba sendo computação, não memória
Claro, ainda estou usando modelos bem menores
Fico curioso se você criou um harness comum para rodar todos eles
Também queria saber quais modelos conseguem ficar dentro do orçamento de VRAM com mais consistência
O llama.cpp suporta várias quantizações de 4 bits ou menos e faz mmap do modelo por padrão, então não é necessário manter todos os pesos na memória
O sistema operacional vai buscá-los no armazenamento quando necessário
É legal ter implementado isso com pouquíssimo código e sem dependências, mas fico curioso se existe alguma vantagem real de desempenho
Bem legal
Mexi com o GLM 5.2 esta semana e também fiquei impressionado
Na empresa, estamos testando execução local em hardware absurdamente caro antes de começar o próximo projeto, então é bom ver gente rodando esse lançamento gigantesco de modelo open source em máquinas mais comuns
Mesmo que ainda não seja muito prático, é um bom trabalho
Não entendo muito bem a estrutura interna dos LLMs, mas fico curioso se algo assim pode ser usado em outros modelos que também tenham “camadas”
Se uma camada termina seu trabalho, será que dá para removê-la da RAM, carregar a próxima do disco e então ativá-la sobre o resultado da primeira?
Minha máquina tem 64 GB de RAM e 24 GB de VRAM, então fico curioso se dá para ajustar isso para usar mais RAM
Ou talvez seja possível rodar Gemma/Qwen na GPU e fazer o GLM-5.2 delegar tarefas menores para eles
Talvez seja preciso retreinar o GLM-5.2 até certo ponto
Também fico pensando se usar vários discos em paralelo para aumentar a largura de banda ajudaria a acelerar
No aviso sobre desgaste de SSD, diz que o cold start é pesado, com cerca de 11 GB/token em leituras aleatórias, e que a leitura em si é segura, mas o cache de páginas do sistema operacional pode gerar escritas, e o uso excessivo pode acelerar o desgaste de SSDs baratos
Uma forma segura talvez seja criar uma partição separada para os pesos do modelo e marcá-la como somente leitura
Não sei se o cache de páginas é por partição ou por disco, mas, se for por disco, talvez dê até para formatar um
data.isosomente leitura como se fosse uma partição e montá-lo como discoSe eu tivesse mais discos, poderia realmente testar isso
Quando tiver benchmarks, poste um pull request ou issue e podemos trabalhar nisso juntos
O que estou deixando passar? Por que grandes volumes de leitura gerariam escrita?
A página tem um aviso sobre desgaste de SSD https://github.com/JustVugg/colibri#ssd-wear-warning
Se você usa um desktop PC montado por conta própria, dá para trocar o SSD, mas e quem usa SSD soldado?
Deve evitar esse tipo de aplicação ou simplesmente aceitar a chance de o armazenamento morrer mais cedo e seguir em frente?
Parece que armazenamento externo teria de ser usado como SSD consumível
Quem tem notebook com SSD soldado precisa monitorar bem o uso e tomar cuidado
Este projeto parece mais um experimento do que algo para todo mundo rodar, mas ainda assim é bem legal
Não precisa tratar isso de forma supersticiosa
Atividade de disco, especialmente escrita, pode ser medida
Por exemplo, no Linux dá para usar
iostatouvmstatO aplicativo não realiza escritas, então, na prática, ele mesmo não desgasta o SSD
O resto se parece mais com recomendações gerais de administração que independem do aplicativo
É tecnicamente impressionante, mas fico curioso se isso é prático de usar na vida real
Eu também estava fazendo basicamente a mesma coisa, mas segui a direção de mapear o modelo inteiro na memória com mmap para evitar uso adicional de RAM
Também fiz o Claude implementar Medusahttps://arxiv.org/abs/2401.10774 no modelo para tentar obter os benefícios da previsão de múltiplos tokens sem carregar um modelo extra na memória
Acabei de chegar em casa e, complementando, todas as modificações estão sendo feitas no llama.cpp, e o objetivo final é colocar isso em algo como um computador de placa única que lide com arquivos mapeados por mmap via NVMe
Com base nos testes anteriores, o limite teórico da configuração atual parece ser de cerca de 1,8 token/segundo, mas os cabeçalhos adicionais do Medusa ainda não estão totalmente treinados, e honestamente nem sei se isso está sendo calculado pelos tokens gerados
No fim, a ideia parece parecida, mas eu ainda não sei escrever um parser/runner de LLM do zero, então, em vez de especificar o que fica na memória, deixei isso a cargo do kernel do Linux
Por fim, limitei o uso do llama.cpp a 16 GB dos meus 32 GB, então talvez dê até para baixar mais