1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • 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.cpp assume 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_GATE e FUSED_UP_GATE incondicionalmente, 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_id e ggml_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.cpp usado para inferência MoE do Gemma 4 é um fork do llama.cpp e, por padrão, assume AVX2 como requisito mínimo
  • Ao desativar GGML_USE_IQK_MULMAT no 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 SwiGLU
    • FUSED_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 case para 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
  • 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 main do fork
  • Correção de compilação sem AVX2

    • Os ramos escalares #else de quantize_row_q8_0_x4 e quantize_row_q8_1_x4_T em iqk_quantize.cpp na prática referenciavam helpers AVX2 como hsum_i32_8
    • Esses ramos foram reescritos como loops escalares portáveis
    • Foram adicionadas guardas #if GGML_USE_IQK_MULMAT a algumas chamadas IQK que haviam vazado para ggml.c e ggml-quants.c
    • Includes ausentes foram adicionados para que iqk_cpu_ops.cpp compilasse de forma independente
    • Sem essa correção, o próprio fork não compila em hardware sem AVX2
  • 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 combinados up_gate_exps quando GGML_USE_IQK_MULMAT está 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_id em 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_id e 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_id usa a implementação padrão do ggml, e fused_mul_unary executa 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 #else do código-fonte IQK divergiam de iqk_mul_mat.h, e por isso o ci/run.sh també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

Custo de performance do fallback e erro de reempacotamento

  • O fallback tem custo extra porque executa mul_mat_id duas 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-repack reorganiza os pesos quantizados no início para o formato intercalado Q8_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_0 també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_MULMAT no 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.cpp do branch com patch sem GGML_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

 
GN⁺ 4 시간 전
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

    • Talvez nem seja preciso esperar até lá. O modelo de pesos ternários Bonsai 27B, que a Prism lançou há alguns dias, tem apenas cerca de 7 GB e passa de 44 tokens por segundo em um notebook M4 Max: https://huggingface.co/prism-ml/Ternary-Bonsai-27B-mlx-2bit
      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
    • 7 a 9 tokens por segundo dificilmente pode ser considerado uma boa velocidade. Não sou especialista, mas, em testes locais, qualquer coisa abaixo de 12 a 16 tokens por segundo pareceu frustrante
    • Concordo em geral com a previsão, mas acho que o prazo pode ser um pouco mais tarde. Para chegar a esse resultado, será necessária uma compressão completamente diferente de simplesmente reduzir o número de bits por parâmetro como hoje; nesse cenário, a própria métrica tradicional de número de parâmetros talvez deixe de fazer sentido
      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 rodando o Ornith 35B em um Pi e obtendo mais de 50 tokens por segundo. Depois que descobri que o Pi também dá suporte a busca e recuperação, parei de procurar modelos grandes para pesquisa
      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
    • 9 tokens por segundo não parece difícil de aguentar? É comum eu usar 1 milhão de tokens em uma sessão do Claude em menos de um dia; para trabalho real, parece lento demais
  • 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

    • Rodando Qwen 27B em casa, ele consome cerca de 400 W em operação, gera aproximadamente 40 tokens por segundo e, mais importante, processa prompts a cerca de 1.000 tokens por segundo
      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
    • Nos EUA, não chega nem perto de US$ 0,30 por kWh. Nos horários de menor demanda é metade disso, e também dá para investir US$ 1.000 em uma bateria para armazenar eletricidade a US$ 0,11 dos horários ultrabaratos
      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
    • Seria legal montar um servidor LLM auto-hospedado e off-grid, alimentado por energia solar e baterias. Algo como a Low Tech Magazine: quando não há sol suficiente, fica offline, e é dimensionado para o uso diário de uma comunidade local — grupo de amigos, rua, clube etc.
      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/
    • Eficiência também importa, mas muita gente tem eletricidade barata ou equipamentos eficientes. Em um home server AMD Strix Halo, dá para servir Gemma4-26B a cerca de 70 tokens por segundo usando apenas 100 W
    • Não me importo se custa mais. O motivo para rodar localmente não é economizar dinheiro, mas obter liberdade e privacidade; além disso, quando existe uma alternativa, os custos caem no longo prazo
      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.

    • Para quem mora numa casa de verdade, é uma conta razoável. Eu moro de aluguel e não pago a conta de luz diretamente, então o limite da eficiência de custo é o momento em que o proprietário começa a reclamar.
    • Acho que a maioria não roda modelos localmente para economizar dinheiro, mas sim para não espalhar informações pessoais.
    • É parecido com criptomoedas. Por causa do custo da energia, para a maioria das pessoas é mais barato comprar do que minerar por conta própria.
    • Nas estações frias, quando você liga o aquecimento em casa, também dá para aproveitar o calor residual, o que melhora a viabilidade econômica.
  • 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

    • Fico curioso se você tentou usar apenas uma CPU para eliminar a queda de desempenho por NUMA. Você acabaria usando só metade da memória, mas quero saber quanta diferença isso faz no desempenho.
    • Parece que, com muita RAM lenta, modelos pequenos também rodam bem rápido. Fico curioso para saber como modelos maiores se comportariam nesse equipamento.
  • É 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

    • Mesmo com um Xeon E3-1270 V2 3,50 GHz e uma Nvidia Quadro K2200 4 GB antiga, sai 8 a 9 tokens por segundo. Estou rodando gemma4:e2b e gemma4:12b-it-qat no Ollama.
    • Não há a diferença de que o post original usa quantização Q8 e aqui se usa Q4?
  • 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.

  • 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