- Em um servidor com dual Xeon E5-2690 v2 de 2013 e DDR3, foi possível rodar o Gemma 4 26B-A4B Q8_0 usando apenas CPU, alcançando cerca de 5,2 tokens/s na decodificação e cerca de 16 tokens/s na avaliação do prompt
- O caminho rápido do
ik_llama.cppassume AVX2·FMA3, mas CPUs Ivy Bridge suportam apenas AVX1, então foram necessárias mudanças de compilação e fallbacks de operação para um build sem AVX2 - O construtor de grafos gerava
MOE_FUSED_UP_GATEeFUSED_UP_GATEincondicionalmente, enquanto o dispatcher sem AVX2 não tinha caminho para tratá-los, fazendo com que cerca de 240 tensores por forward pass ficassem sem ser calculados e resultassem em saídas multilíngues sem sentido - Claude encontrou o erro com instrumentação de logits e análise de código, substituindo as operações fundidas por duas chamadas de
ggml_mul_mat_ideggml_fused_mul_unary, enquanto o usuário executava os experimentos e determinava o critério de resultado correto - Mesmo servidores corporativos antigos podem servir como alternativa local em caso de falha de APIs pagas ou para jobs em lote lentos, mas não se deve usar
--run-time-repack, que cria um layout exclusivo para AVX2
Ambiente de execução em um servidor de storage de 13 anos
- O equipamento HP StoreVirtual reaproveitado usa dual Xeon E5-2690 v2 de 2013 e memória DDR3, sem GPU
- Por ser da geração Ivy Bridge, suporta apenas AVX1 e não suporta AVX2 nem FMA3
- Foi originalmente feito para armazenamento em disco e custou menos de 300 dólares
- O modelo executado é o Gemma 4 26B-A4B do Google, um modelo aberto de mistura de especialistas (MoE) com 26 bilhões de parâmetros
- No modelo quantizado Q8_0, foram medidos cerca de 5,2 tokens/s na decodificação e cerca de 16 tokens/s na avaliação do prompt
O ponto de partida: um caso com Xeon de 2016
- O texto compartilhado no Hacker News, A 10 year old Xeon is all you need, mostra a execução do Gemma 4 sem GPU em um Xeon único de 2016 com 128GB de DDR3
- Essa configuração usa ik_llama.cpp e cerca de 25 flags detalhadas de execução
- decodificação especulativa
- roteamento MoE voltado para CPU
- flash attention para CPU
- reempacotamento de pesos em tempo de execução
- A mesma abordagem foi aplicada ao servidor Ivy Bridge, mas a execução falhou logo no início
- Diferentemente da CPU Broadwell de 2016, o E5-2690 v2 não tem AVX2 nem FMA3
- Esse conjunto de instruções passou a existir a partir do Haswell de 2014, ou seja, da geração v3 da Intel, e os kernels rápidos foram escritos assumindo isso
Diagnóstico e patch com Claude
- Ao receber as informações da falha de inicialização, Claude identificou como causa a diferença no conjunto de instruções da CPU
- Dando continuidade a uma tentativa incompleta feita antes com um modelo gratuito, ele corrigiu os caminhos de C++ críticos para performance para que fizessem fallback corretamente em CPUs anteriores ao AVX2
- O trabalho não terminou com um único pedido de
fix it- Leu código C++ focado em performance escrito por outro desenvolvedor
- Analisou por que certos kernels não eram válidos em uma microarquitetura específica
- Contornou caminhos não suportados sem descartar as otimizações já existentes no fork
- Em vez de escrever os fallbacks dos kernels C++ diretamente, o usuário ficou responsável por executar os experimentos, ler as saídas, formular a próxima pergunta e determinar o critério de resultado correto
- O diagnóstico e o patch foram feitos por uma instância do Claude rodando nesse próprio servidor
Caminho de operações quebrado em build sem AVX2
- O
ik_llama.cppusado para inferência MoE do Gemma 4 é um fork dollama.cppe, por padrão, assume AVX2 como requisito mínimo - Ao desativar
GGML_USE_IQK_MULMATno build, a maior parte dos caminhos rápidos é removida e ocorre fallback para operações escalares/SSE genéricas- Esse fallback funciona para multiplicação de matrizes Q8_0 comum
- Porém, ele não se aplicava a duas operações do grafo
- A rede feedforward MoE do Gemma 4 gera as seguintes operações
MOE_FUSED_UP_GATE: operação fundida que combina multiplicações de matrizes gate·up por especialista e SwiGLUFUSED_UP_GATE: versão densa da operação, usada em camadas sem MoE
- As duas operações do dispatcher de computação estavam protegidas por
GGML_USE_IQK_MULMAT, mas o construtor de grafos criava as operações sem condição- No dispatcher do build sem AVX2, não havia
casepara tratar esses enums - As operações caíam no ramo padrão, e os tensores de destino de todas as redes feedforward dos especialistas simplesmente deixavam de ser calculados
- No dispatcher do build sem AVX2, não havia
- Como o Gemma 4 26B usa 8 especialistas ativos por token em 30 camadas, ele passava a consumir cerca de 240 tensores contendo valores que haviam sobrado nos buffers de memória a cada forward pass
Pistas reveladas pela saída sem sentido
- A saída com erro parecia fluente, mas era uma sequência multilíngue sem significado
- Os IDs de token se espalhavam de forma uniforme por todo o vocabulário de 262 mil entradas
- Tailandês, coreano, sentinelas
<unused>e fragmentos em inglês eram gerados com frequências parecidas
- Com temperatura 0, a saída era determinística, e os resultados com uma thread e múltiplas threads eram idênticos byte a byte, além de não haver NaN
- Em cada camada, grandes constantes empurravam o estado oculto, achatando o softmax final
- Claude instrumentou os logits brutos antes da amostragem para imprimir os 5 tokens principais, faixa, média e quantidade de NaNs
- A média dos logits do primeiro token previsto não ficava perto de 0, mas sim em +16
- Cerca de 80% de todo o vocabulário tinha logits positivos
- Como o viés era consistente, e não corrupção aleatória, concluiu-se que uma grande parte do estado oculto estava usando memória não inicializada que continha pequenos valores positivos de ponto flutuante
Correção em três etapas
- O patch é composto por três commits acima do
maindo fork -
Correção de compilação sem AVX2
- Os ramos escalares
#elsedequantize_row_q8_0_x4equantize_row_q8_1_x4_Temiqk_quantize.cppna prática referenciavam helpers AVX2 comohsum_i32_8 - Esses ramos foram reescritos como loops escalares portáveis
- Foram adicionadas guardas
#if GGML_USE_IQK_MULMATa algumas chamadas IQK que haviam vazado paraggml.ceggml-quants.c - Includes ausentes foram adicionados para que
iqk_cpu_ops.cppcompilasse de forma independente - Sem essa correção, o próprio fork não compila em hardware sem AVX2
- Os ramos escalares
-
Fallback de grafo em tempo de execução
- Em vez de corrigir o dispatcher, o construtor de grafos foi alterado para gerar operações que já possuem caminho de computação nesse build
- Em
ggml_moe_up_gate, ele trata os pesos combinadosup_gate_expsquandoGGML_USE_IQK_MULMATestá desligado - O formato do tensor é
[n_embd, 2*n_ff, n_experts], com a primeira metade sendo gate e a segunda sendo up - Ele separa isso em duas fatias
ggml_view_3d - Executa
ggml_mul_mat_idem cada fatia - Combina os dois resultados com
ggml_fused_mul_unary(gate, up, SILU) - Se os pesos de gate e up já estiverem separados, usa duas chamadas de
ggml_mul_mat_ide a operação fundida de multiplicação/unária sem fazer slicing - O mesmo fallback foi aplicado a
ggml_fused_up_gate, usado em camadas sem MoE mul_mat_idusa a implementação padrão do ggml, efused_mul_unaryexecuta SILU e multiplicação de uma vez, então todas as operações componentes já têm implementação sem IQK- Toda a mudança fica atrás de
#if !GGML_USE_IQK_MULMAT, então o resultado em builds com AVX2 continua idêntico bit a bit
-
Ajuste dos stubs de CI
- Os stubs
#elsedo código-fonte IQK divergiam deiqk_mul_mat.h, e por isso oci/run.shtambém não compilava em hardware sem AVX2 - Faltava o include de
<cstdint> - Alguns stubs tinham parâmetros iniciais desnecessários ou não incluíam o parâmetro
sinks, então suas assinaturas eram diferentes - Algumas funções nem sequer tinham stub, causando referências indefinidas na etapa de linkedição
- Ao alinhar os stubs com o header, passou a ser possível executar a suíte de testes também em ambientes sem AVX2
- Os stubs
Custo de performance do fallback e erro de reempacotamento
- O fallback tem custo extra porque executa
mul_mat_idduas vezes em vez de um kernel fundido único - Essa CPU já está limitada por largura de banda de memória, e o kernel fundido original também era exclusivo de AVX2, então não havia um caminho de execução anterior a ser perdido nesse ambiente
- A performance final foi de cerca de 5,2 tokens/s na decodificação e cerca de 16 tokens/s na avaliação do prompt no MoE 26B-A4B
--run-time-repackreorganiza os pesos quantizados no início para o formato intercaladoQ8_0_R8, exclusivo de AVX2- Esse é um bug separado que volta a corromper a saída em ambiente AVX1
- O patch atual não corrige isso, e a flag foi removida do script de execução
Como a causa foi sendo isolada
- A incompatibilidade de conjunto de instruções foi fácil de confirmar, mas o fato de o dispatcher cair silenciosamente no ramo padrão sem erro foi mais difícil de descobrir
- A revisão do código permitiu eliminar vários suspeitos
- Os helpers de RMSNorm pareciam corretos
- O fallback AVX1 de
ggml_vec_dot_q8_0_q8_0também parecia correto - Como o resultado em thread única era idêntico bit a bit, erro de threading foi descartado
- O fato de a média dos logits ficar fixa em +16 e tokens de cauda longa apresentarem valores parecidos levou à conclusão de que uma grande parte do fluxo residual não estava inicializada
- Depois de buscar por
#if GGML_USE_IQK_MULMATno dispatcher, os dois caminhos de operação ausentes foram encontrados em cerca de um minuto
Condições de reprodução e escopo de uso
- As condições de reprodução em hardware anterior ao AVX2 são as seguintes
- Hardware: dual Xeon E5-2690 v2, Ivy Bridge, AVX1, DDR3, sem GPU
- Build: compilar o
ik_llama.cppdo branch com patch semGGML_USE_IQK_MULMAT - Modelo: Gemma 4 26B-A4B Q8_0
- Execução: usar as flags normais de CPU do
ik_llama.cpp, mas sem--run-time-repack
- As mudanças exatas podem ser vistas em ikawrakow/ik_llama.cpp PR #2138
- No momento da escrita, ela ainda estava aberta e aguardava revisão dos mantenedores, então é preciso rodar direto do branch
- Bugs observados no mesmo hardware podem ser reportados na thread da PR
- Manter um modelo local em servidores corporativos antigos pode servir como alternativa quando uma API paga sai do ar, ou para processar jobs em lote lentos em que a cobrança por token não faz sentido
- A ideia valoriza mais a capacidade de investigar código desconhecido e sistemas antigos do que simplesmente usar um serviço por assinatura, e o mesmo tipo de habilidade também se aplica à manutenção de uma aplicação Rails com 15 anos ou de um banco de dados cujo responsável original já saiu
1 comentários
Opiniões no Hacker News
A expectativa é que, em meados de 2027, seja possível rodar modelos de mistura de especialistas (MoE) com mais de 200 bilhões de parâmetros em hardware de consumo comum
Estou rodando o Qwen3.6-35B-A3B localmente em um Mac de 16 GB e obtendo 7 a 9 tokens por segundo: https://github.com/deepanwadhwa/samosa-chat
Ou seja, um modelo de nível GPT-4 rodando localmente nessa velocidade em um MacBook Air com 16 GB de RAM
Isso já está na mesma faixa do número de parâmetros ativos da maioria dos modelos com mais de 200 bilhões, então, se a Prism quiser, pode lançar um modelo desses. Dito isso, redes neurais recorrentes como o HRM não precisam de tantos parâmetros, então é discutível se uma escala dessas é realmente necessária: https://huggingface.co/sapientinc/HRM-Text-1B
Por exemplo, se cada camada tiver apenas uma seed de 256 bits e ela for passada por uma função de ruído para gerar 16K pesos reais, o armazenamento por peso pode cair para menos de 1 bit
Estou animado porque devem sair, neste mês e no próximo, vários modelos novos capazes de mudar o jogo. Vale muito a pena experimentar o Ornith
Pode haver quem não goste de ouvir isso, mas acho que o custo por token dos provedores de inferência é menor do que o custo de eletricidade para rodar localmente
Simplificando e contando só a geração de saída, 5 tokens por segundo dão 18.000 tokens por hora, e o custo no provedor é de cerca de US$ 0,005. Considerando que o servidor consome cerca de 500 W durante a inferência e usando como referência a Alemanha, onde a eletricidade custa US$ 0,30 por kWh, gerar a mesma quantidade localmente sai por US$ 0,15, ou seja, 30 vezes mais caro
Se a preocupação for privacidade, rodar localmente ainda é uma boa opção, mas é preciso saber que isso é muito menos eficiente do que usar um provedor de inferência. À medida que novas GPUs aumentarem a eficiência da inferência, essa diferença deve crescer bastante
No começo, calculei errado como 180 mil tokens, mas na verdade são 18.000 tokens; então, a menos que você tenha eletricidade quase de graça, é difícil competir. Os provedores provavelmente ainda usam H200/H100 em modelos pequenos, mas, quando migrarem para GB300 ou para a GPU Ruby do ano que vem, o custo de inferência deve cair para 1/30, e o principal valor dos modelos locais provavelmente será a privacidade
Em uma hora, isso processa 3,6 milhões de tokens de entrada ou gera 144.000 tokens, com cerca de US$ 0,15 de eletricidade. Fazer a mesma quantidade no Sonnet custaria US$ 7,20 para entrada e US$ 1,40 para geração, então a nuvem é 10 vezes mais cara na geração e quase 50 vezes mais cara no processamento
Os provedores de inferência estão competindo por participação de mercado carregando dívidas enormes, então os preços certamente vão subir
Se democratizarmos a IA a ponto de a comunidade controlá-la diretamente, isso também resolve o problema dos data centers, e os níveis de censura e alinhamento podem ser decididos democraticamente pela própria comunidade. É uma ideia parecida com alguns textos do Geohotz
Modelos open source logo ficarão bons e eficientes o suficiente para serem servidos de forma barata em hardware usado. Em cada pequena comunidade, uma pessoa com conhecimento técnico poderia arrecadar algumas centenas de dólares de custo inicial e depois operar quase de graça, sem custo de eletricidade
https://solar.lowtechmagazine.com/
Se não houvesse a alternativa local, os preços dos modelos em nuvem seriam muito mais altos
É bem provável que Xeons duplos dessa geração consumam mais de 300 W sob carga. Pela tarifa média de eletricidade dos EUA, isso dá US$ 1,35 por dia, e fica mais caro se você precisar refrigerar o ambiente no verão.
Mesmo ignorando o tempo de processamento do prompt e rodando 24 horas, são cerca de 400 mil tokens por dia, então o custo fica em torno de US$ 0,30 por 1 milhão de tokens de saída. Por coincidência, é o mesmo preço atual desse modelo no OpenRouter, mas a velocidade de geração do OpenRouter é 8 vezes maior.
Há muitos motivos para experimentar LLMs locais, como impedir que os dados saiam de casa, mas é difícil obter vantagem financeira. Como alguém que investiu muito mais dinheiro em equipamento local de inferência em casa, acho divertido, mas não vejo como forma de economizar.
Compilei resultados de execução de vários modelos em Xeon duplo e 256 GB de DDR4, sem GPU.
https://gist.github.com/hparadiz/f3596d00a62d8ebb2dadcc46ee5822c7
É bem lento. Mesmo em uma CPU de 13 anos, estou conseguindo 8 a 12 tokens por segundo, dependendo do tamanho do contexto e de outras configurações.
https://news.ycombinator.com/item?id=48354801
Sou o autor do post original. Parece que o comentário original foi sinalizado por algum motivo. Abri as correções como PR #2138 no projeto upstream: https://github.com/ikawrakow/ik_llama.cpp/pull/2138
Esse equipamento StoreVirtual não parece ter uma porta adequada para instalar software. Parece ter só portas USB; fico curioso se a instalação é feita via console serial.
Estou rodando o Gemma 4 26B em um Mac Pro de 2013 com a mesma configuração e obtendo cerca de 5 tokens por segundo. As GPUs duplas não servem para isso, mas para algumas tarefas ainda é suficientemente prático.
Ele chega na sexta-feira, então estou animado para testar por conta própria.
https://echalupa.com/blog/mac-pro-6-1-llama-cpp-firepro-d300-vulkan-ubuntu
https://matthewgribben.com/blog/mac-pro-6-1-llama-cpp-firepro-d700-vulkan-ubuntu
Vi pessoalmente a configuração no porão e é realmente incrível. Na próxima, seria legal mostrar também a impressora 3D.
Um vídeo meio relacionado: rodaram um LLM em um Pentium 4 e deram o apelido de NetburstGPT. Claro que é muito lento.
https://www.youtube.com/watch?v=ILV-eu90te8