Um Qwen 80B em 4,3 GB de RAM: como o Swiftlet faz streaming dos experts pelo SSD (e o que isso custa em velocidade)
Por Devy · Categoria: AI Dev Tools — General
O número do título é real, e vale a pena dizê-lo com precisão antes de fazer qualquer coisa com ele: Swiftlet roda Qwen3-Next-80B-A3B com 4,3 GB de RAM de pico, com 42 GB apoiados em disco, a 4,5–5 tokens por segundo — medido em um Mac M5. O irmão de 35B, Qwen3.6-35B-A3B, fica em 2,6 GB de RAM, 18 GB de disco e 7–11 tok/s na mesma máquina. E sim, o 35B roda em um iPhone 17, com cerca de 2,5 GB e aproximadamente 1 tok/s.
Essas são cifras de decode sobre Apple Silicon, e o M5 importa: não é um número que você possa transpor mentalmente para um M1 Air e esperar que se sustente. Mas o interessante não é o benchmark. É que o número te diz onde o modelo entra, não tão rápido quanto roda, e o próprio autor do Swiftlet é refrescantemente claro sobre essa diferença. Vamos instalá-lo, rodá-lo e depois falar honestamente sobre a parte que o título deixa de fora.
O mecanismo, em um parágrafo
Swiftlet é um runtime em Swift + Metal, Apache-2.0, para a família MoE Qwen3-Next / Qwen3.6. O truque é uma separação que as arquiteturas MoE convidam a fazer, mas que a maioria dos runtimes não aproveita: mantém residente em memória apenas o núcleo denso — attention, as projeções de DeltaNet, os roteadores, os shared experts, os embeddings — e deixa os routed experts no SSD. Quando um token precisa de um expert, Swiftlet emite um único pread para trazê-lo para um pool de cache limitado, com desalocação por LFU mais recência.
O detalhe que habilita isso é o formato do arquivo. Swiftlet reempacota o modelo em um contêiner .qpack que guarda os experts como blobs de stride fixo, então qualquer expert é uma única leitura posicionada em um offset calculável — sem memory mapping, sem loteria de page faults, sem procurar dentro de um checkpoint do HuggingFace. Por isso a cifra de RAM é um limite e não uma média: o conjunto residente é o núcleo denso mais um cache que você dimensiona.
Instalar e rodar
Você precisa de Apple Silicon, macOS 14+ (ou iOS 17+) e SSD livre suficiente para alojar o modelo.
1. Clone e compile.
git clone https://github.com/leonickson1/Swiftlet.git && cd Swiftlet
swift build -c release
2. Reempacote o modelo para .qpack. Este é o passo que converte o checkpoint para o layout de stride fixo que o caminho de streaming precisa. Você pode trazê-lo diretamente do HuggingFace:
.build/release/swiftlet-repack \
--from-hf Leonickson/Qwen3.6-35B-A3B-qpack \
--output ~/models/qwen3.6-35b.qpack
Há um flag --source se você já tiver um checkpoint local.
3. Converse.
.build/release/swiftlet chat ~/models/qwen3.6-35b.qpack "Your prompt"
4. Ou suba como servidor. Esta é a parte que faz o Swiftlet entrar em um setup existente em vez de substituí-lo:
.build/release/swiftlet-server --model ~/models/qwen3.6-35b.qpack --port 8080
swiftlet-server fala a API de chat-completions do OpenAI apenas em loopback, não sobre a rede. Qualquer chat UI que converse com um endpoint compatível com OpenAI aponta para lá e obtém geração local em streaming. Comece pelo 35B: são 18 GB em vez de 42 e aproximadamente o dobro de velocidade de decode, e vai te dizer se a abordagem completa se encaixa no seu fluxo de trabalho antes de você comprometer o disco.
Integrá-lo em um app. Também há um caminho como Swift package: adicione SwiftletCore a um target de macOS ou iOS e gerencie-o através de SwiftletSession, que já resolve streaming, cache de conversa e sampling.
Sobre o iPhone
A afirmação do iPhone é verdadeira e merece uma esclarecimento, porque “um 35B em um iPhone” convida à imagem mental errada. Você não compila o CLI no seu telefone. O caminho é o app Priv AI na App Store, ou compilar do repo leonickson1/localLLM com o Swiftlet clonado ao lado. E a velocidade é aproximadamente 1 token por segundo: é um resultado de “o modelo se encaixa fisicamente e gera”, não de “substitui seu app de chat”. O que, tratando-se de um modelo de 35 bilhões de parâmetros rodando em um telefone, ainda é a versão impressionante da frase.
Agora a parte honesta: a RAM nunca foi o gargalo
Este é o achado em torno do qual eu configuraria suas expectativas, e vem do próprio autor na thread do Hacker News.
A RAM é ajustável: você pode dar mais memória ao cache. Então alguém fez a pergunta óbvia: dar mais ajuda? O autor testou. Passar de um cache de 1 GB para um de 6 GB moveu a taxa de acerto do cache de experts de 43% para 70% — e deixou o throughput essencialmente igual. O gargalo não são as leituras do SSD nem os cache misses. É o dispatch de GPU. O que significa que o número de eficiência do título é, em certo sentido, gratuito: você não está trocando velocidade por esse conjunto residente pequeno, porque o conjunto residente nunca foi o que o estava freando. Também significa que o que vai tornar o Swiftlet significativamente mais rápido é trabalho de kernels, não mais memória.
A segunda ressalva é maior e o README não a cobre em absoluto: não há números de prefill publicados. Todas as cifras acima são de decode — tokens saindo. O processamento do prompt é um custo separado, e na thread de HN um comentarista fez as contas e chegou a aproximadamente meia hora para processar 10k tokens em um M5. Tome isso como uma estimativa não verificada de um leitor e não como um benchmark medido, mas o ponto estrutural se sustenta e é importante: se seu caso de uso é “grudo um arquivo grande e faço perguntas sobre ele”, o prefill é a parede com a qual você vai colidir, e é o número que ninguém está citando. Prompts curtos e gerações longas é onde essa arquitetura se sente confortável.
E uma terceira, que o README diz sem rodeios e repito porque é uma daquelas coisas que se descobre pelo caminho incômodo: apenas cerca de 3B parâmetros estão ativos por token. Como coloca a documentação, esses modelos “conversam e escrevem como modelos grandes, mas lembram fatos como modelos pequenos”. Um MoE de 80B com 3B ativos não é um modelo denso de 80B disfarçado. O raciocínio e a fluidez se sustentam; a memória enciclopédica não.
A parte de que realmente quero falar: a seção de créditos
Há uma seção no README do Swiftlet chamada “Relationship to TurboFieldfare”, e me parece a coisa mais silenciosamente interessante do repo.
Em 31 de julho cobrimos TurboFieldfare, o runtime em Swift que meteu o Gemma 4 26B em ~2 GB de RAM fazendo streaming dos experts pelo SSD. Swiftlet é a mesma ideia apontada para outra família de modelos, e em vez de reimplementá-la em silêncio ou proclamar novidade aos gritos, o README faz algo que quase nunca se vê: enumera quais lições de design adotou e qual código escreveu do zero.
O que diz ter adotado do TurboFieldfare: fazer streaming de experts com pread para um pool de slots limitado; desalocar com LFU mais recência; empacotar os experts com stride fixo; compilar os shaders em runtime. O que declara como novo: uma arquitetura inteiramente distinta para suportar a linear attention Gated DeltaNet do Qwen, o caminho de computação quantizado, a infraestrutura de validação e a integração com iOS.
Essa é uma distinção real, e vale a pena nomeá-la justamente porque nossa indústria hoje é ruim nisso. As ideias viajam; o código não precisa. TurboFieldfare provou que o expert streaming era viável — que o SSD podia fazer de substituto para a RAM em um MoE sem que tudo desabasse. Uma vez estabelecido isso, o próximo não precisa do código do primeiro, precisa de suas conclusões, e Gated DeltaNet é suficientemente diferente da attention de Gemma para que uma implementação nova fosse a única opção honesta de qualquer forma. Escrever qual é qual não custa nada ao autor e dá a cada leitor um mapa preciso de onde o trabalho ocorreu.
Se você está publicando um projeto que se baseia no de outra pessoa, este é o template. Não uma linha de Thanks to X for the inspiration no final. Uma seção que diz: aqui estão as quatro decisões de design que eu tomei, e aqui está tudo o que eu escrevi.
Para levar
Swiftlet vale uma tarde se você está em Apple Silicon e quer um MoE genuinamente grande rodando local sem uma máquina de 64 GB. Comece pelo 35B, rode-o atrás de swiftlet-server e aponte sua UI de chat existente para loopback. Entre esperando um modelo que encaixa lindamente e gera em uma taxa que serve ao trabalho tolerante a latência: resumos em segundo plano, processamento em lote, qualquer coisa onde você não esteja olhando o cursor. Não entre esperando colocar um arquivo de 10k tokens.
E leia a seção de créditos antes dos benchmarks. É a melhor peça de escrita de engenharia das duas.
E você, já testou rodar um MoE grande fazendo streaming do SSD? Em qual caso de uso a velocidade de decode que se consegue realmente te serviu — e em qual o prefill te freou?
Fontes: