RTX 5090 e MacBook Air com M4: dá para jogar?
(scottjg.com)- Um eGPU RTX 5090 foi conectado a um MacBook Air com M4 para rodar jogos em uma VM Linux, exigindo contornar a falta de drivers no macOS e as limitações do Thunderbolt
- A implementação exigiu um dispositivo PCI virtual
apple-dma-pci, contorno do mapeamento DART, patch no driver da NVIDIA com base em kprobes e modificações no QEMU/HVF - Cyberpunk 2077 no M4 Air + eGPU alcançou 27 fps em 4K RT Ultra e ficou jogável com até 111 fps usando geração de frames por DLSS
- Com a mesma RTX 5090 em um PC PCIe comum, o desempenho foi 2 a 4 vezes maior dependendo do jogo, com bastante overhead de FEX, Proton, VM e Thunderbolt
- O ganho maior veio em inferência de IA: o prefill do Qwen no M4 Air caiu cerca de 100 vezes, e a geração em stream único subiu de aproximadamente 22 tok/s para 155 tok/s
Restrições de eGPU Thunderbolt e Macs com Apple Silicon
-
Estrutura de eGPU via Thunderbolt
- Uma GPU desktop como a RTX 5090 é conectada a um dock Thunderbolt, e o dock converte PCIe em Thunderbolt para ligar à porta USB-C do MacBook Air com M4
- Como o Thunderbolt faz tunelamento de PCIe sobre o cabo USB-C, para o computador o dispositivo Thunderbolt aparece como um dispositivo PCIe, não USB
- Thunderbolt 4 oferece até 40 Gbps com 4 lanes PCIe, com uma pequena perda de desempenho por causa do tunelamento
- Em geral, no Linux e no Windows, a eGPU é reconhecida como um dispositivo PCIe um pouco mais lento e quase funciona de imediato com drivers existentes
- O primeiro obstáculo era que o macOS em Apple Silicon não fornece drivers da NVIDIA nem da AMD por padrão
-
Contorno via VM Linux
- Rodar Linux em Macs com Apple Silicon é possível, mas o kernel Linux atual ainda não suporta Thunderbolt em Apple Silicon, apenas dispositivos internos e USB3
- Ao rodar uma VM Linux ARM de 64 bits sobre o macOS host, torna-se possível combinar o macOS lidando com o dispositivo Thunderbolt e o Linux lidando com a GPU da NVIDIA
- Como não existe driver de placas NVIDIA para Windows ARM64, foi usado Linux
- O driver eGPU para macOS do tinygrad só funciona dentro da stack do tinygrad, então não servia como driver genérico para inferência de IA nem para rodar jogos
Implementando PCI passthrough no macOS
-
PCI BAR e DMA
- Para que a VM se comunique com um dispositivo PCI, os PCI BARs (Base Address Registers) e o DMA precisam funcionar
- Os PCI BARs são regiões de memória que o dispositivo PCI usa para ler e escrever no computador, e elas precisam ser espelhadas dentro da VM para o passthrough funcionar
- DMA (Direct Memory Access) é o mecanismo pelo qual o dispositivo lê e escreve diretamente na memória do computador sem cópia via CPU
-
Hypervisor.framework e mapeamento de BAR
- Ao iniciar a VM, o QEMU chama
Hypervisor.frameworkviahv_vm_map()para configurar o layout de memória do guest - Ao se conectar ao driver host PCIDriverKit e mapear a memória do dispositivo PCI para o processo com
IOConnectMapMemory64(), é possível ler a memória BAR0 - Ao ler
BAR0[0], sai0x1b2000a1, uma constante específica do dispositivo que aponta para a RTX 5090 - Se o QEMU mapear a memória do dispositivo diretamente no guest, o guest pode falar com a GPU diretamente, mas no início o kernel host travava assim que a VM tocava a memória PCI BAR
- A causa era que o QEMU adicionava
HV_MEMORY_EXECtambém ao mapeamento de RAM-device/MMIO; removerEXECnos mapeamentos device/MMIO evitou o panic no host - Esse método é cerca de 30 vezes mais rápido do que interceptar todo acesso, mas como
hv_vm_map()não permite definir o subtipo de memória de dispositivo no ARM, as escritas no BAR ficam cerca de 10 vezes mais lentas do que no caso ideal
- Ao iniciar a VM, o QEMU chama
Contornando restrições de DMA e DART
-
Limites do DART no Apple Silicon
- O Apple Silicon tem uma unidade de hardware DART, parecida com um IOMMU, que também funciona como barreira de segurança para impedir que dispositivos acessem memória arbitrária do host
- No uso de dispositivos Thunderbolt via PCIDriverKit, havia três limitações importantes
- O limite de mapeamento de cerca de 1,5 GB impede manter diretamente os mapeamentos de 8 a 16 GB exigidos por CUDA e jogos modernos
- O limite de cerca de 64 mil mapeamentos faz a tabela esgotar se houver muitos buffers DMA pequenos
- Como não é possível escolher diretamente endereço e alinhamento, um IOMMU virtual em que o guest define o endereço DMA não se encaixa bem
- Forçar o DMA para uma região pré-alocada com
restricted-dma-poolfuncionou em dispositivos simples, mas não combinou com o driver da GPU
-
Dispositivo PCI virtual
apple-dma-pci- Foi adicionado ao QEMU um dispositivo PCI virtual chamado
apple-dma-pci, inserido na VM junto com a GPU passada via passthrough - Um driver no kernel guest intercepta as chamadas de mapeamento DMA do driver da NVIDIA, cria mapeamentos sob demanda para cada requisição DMA e os remove quando o buffer é liberado
- Nessa arquitetura, não é preciso que toda a RAM do guest caiba no limite de 1,5 GB; basta que o conjunto ativo de buffers DMA daquele momento fique dentro dele
- O driver do guest substitui o driver da NVIDIA por operações DMA customizadas antes de a GPU ser usada, tratando
map_page,unmap_page,map_sg,unmap_sg,allocefreecomo wrappers finos - No lado host, o QEMU repassa a requisição ao driver PCIDriverKit, que faz o mapeamento real no DART e grava de volta o endereço DMA na memória do guest
- Foi adicionado ao QEMU um dispositivo PCI virtual chamado
-
Problema de alinhamento da NVIDIA e patch com kprobes
- Durante cargas CUDA, apareceram logs de kernel com
NV_ERR_INVALID_OFFSETe erros de alinhamento relacionados aRM_PAGE_SIZE_HUGE - A alocação problemática era do tipo
UVM_RM_MEM_TYPE_SYS, com 16 MB, e o driver da NVIDIA entrava em conflito com as restrições de alinhamento ao usar páginas de 2 MB - O driver já tinha um contorno para tentar novamente com o tamanho padrão de página quando ocorria
NV_ERR_NO_MEMORY, mas não tratavaNV_ERR_INVALID_OFFSET - Usando kprobes do kernel Linux, a chamada
nvUvmInterfaceMemoryAllocSys()foi interceptada para forçarpageSizeaUVM_PAGE_SIZE_DEFAULT - Sem modificar diretamente o driver da NVIDIA, isso passou a fazer o driver pedir páginas menores e permitiu que workloads simples funcionassem
- Durante cargas CUDA, apareceram logs de kernel com
-
Coalescência de mapeamentos para escapar do limite de 64 mil
- Ao aumentar os gráficos dos jogos, surgiam muitos mapeamentos pequenos, passando do limite de cerca de 64 mil mapeamentos
- Nos logs, mais de 90% dos mapeamentos tinham 4 kB e apareciam em clusters, embora não fossem contíguos
- A memória do guest foi dividida em regiões fixas de 256 kB, e quando um buffer de 4 kB é mapeado, todo o cluster correspondente é mapeado para que futuras alocações no mesmo cluster reutilizem o mapeamento existente
- Isso mapeia um pouco mais de memória do que o conjunto de buffers realmente ativos, mas nos testes continuou abaixo do teto de cerca de 1,5 GB
- O número de mapeamentos ativos caiu em torno de 4 vezes, abrindo margem para rodar jogos mais exigentes no máximo
Melhorias de desempenho da VM
-
Escalonamento de vCPU
- Havia um problema em que scores de benchmark variavam muito aleatoriamente e às vezes saíam 50% mais lentos
- A suspeita era de que o escalonador não estava dando tempo suficiente de execução para a VM porque o QEMU não definia prioridade para a thread de vCPU
- Foi aplicado um patch no caminho de inicialização da vCPU do QEMU usando
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0)epthread_setschedparam(..., SCHED_RR, ...)para atribuir prioridade alta
-
Total Store Ordering e FEX-Emu
- A VM roda Linux arm64, mas a maioria dos jogos distribuídos comercialmente é binário x86-64 para Windows, então foram usados Proton) e FEX-Emu juntos
- A arquitetura x86 usa Total Store Ordering (TSO), em que stores são vistos por outros cores na ordem do programa, enquanto o ARM usa um modelo de ordenação de memória mais fraco
- O Apple Silicon tem um modo de hardware TSO por thread, exposto pelo
Hypervisor.frameworkno macOS 15+ - Foi aproveitado um patch do UTM para ligar o bit 1 de
ACTLR_EL1na vCPU e desativar a emulação de TSO por software do FEX-Emu - Se a emulação de TSO por software for desligada sem o bit de hardware, o Geekbench 6 trava no meio da execução
- O desempenho do guest com FEX e TSO na CPU ficou cerca de 50% abaixo do desempenho nativo do host
Desempenho em jogos
-
Cyberpunk 2077
- Cyberpunk 2077 foi testado no M4 Air com macOS nativo, no M4 Air + eGPU, em um MacBook Pro Intel 2020 + eGPU, no M5 Max com macOS nativo, no M5 Max + eGPU e em um PC gamer i5-12600K + RTX 5090
+ Framegenusa DLSS 4x nas configurações com eGPU/PCIe nativo e FSR 2x nas configurações nativas do macOS- Em 720p Low, a carga na GPU era pequena, então CPU e overhead de emulação/virtualização dominavam; o M4 Air nativo fez 61 fps contra 49 fps no M4 Air + eGPU
- Em 1080p High, o M5 Max nativo no macOS fez 131 fps, superando os 68 fps do M5 Max + eGPU; quando a GPU integrada já dá conta, o overhead pesa mais
- O M4 Air nativo no macOS ficou em apenas 7 fps em 1080p RT Ultra, enquanto o M4 Air + eGPU marcou 30 fps e chegou a 119 fps com geração de frames
- Em 4K, o gargalo passa a ser a GPU; o M4 Air + eGPU atingiu 27 fps em RT Ultra e 111 fps com geração de frames por DLSS, alcançando nível jogável
- O PC gamer com PCIe nativo foi o mais rápido, com 100 fps em 4K RT Ultra e 282 fps com geração de frames por DLSS
- O M5 Max + eGPU fez 47 fps em 4K RT Ultra e 145 fps com geração de frames, cerca de 30% a 70% mais rápido que o M4 Air + eGPU
-
Outros jogos e benchmarks
- No GravityMark, ligar a mesma GPU via Thunderbolt em vez de slot PCIe reduziu o desempenho em cerca de 20%, e o M4 Air + eGPU ficou cerca de 31% abaixo da conexão PCIe nativa
- Em Shadow of the Tomb Raider, a eGPU elevou o M4 Air de 8 fps nativos em 4K para 40 fps, e em 1080p de 26 fps nativos para 42 fps com eGPU
- O desempenho da eGPU em Shadow of the Tomb Raider ficou quase igual entre 1080p e 4K, mostrando que o gargalo não era a GPU, e sim a CPU sob o FEX
- Horizon Zero Dawn Remastered exigia mais de 1,5 GB de mapeamentos DMA de uma vez mesmo no mínimo em 720p, então o benchmark nem chegou a iniciar
- Doom (2016) funcionou na configuração com eGPU e marcou 49 fps no overlay de desempenho, mantendo sempre mais de 30 fps
- Crysis Remastered foi até cerca de 4 vezes mais lento do que no PC gamer, mas ainda rodou com taxa de quadros jogável no MacBook Air com M4
Desempenho em inferência de IA
-
Qwen 3.6
- O teste usou o modelo Qwen 35B MoE quantizado em 4 bits, com 3B de parâmetros ativos
- Na GPU da NVIDIA foi usada a versão vLLM com NVFP4, enquanto no Apple Silicon foram usados vllm-mlx e um modelo quantizado em 4 bits no MLX
- O benchmark foi executado com
llama-benchy - A eGPU via Thunderbolt perdeu cerca de 9% de desempenho em relação ao PCIe, mas como a maior parte do processamento acontece na placa, o resultado ficou bem próximo do PCIe
- A RTX 5090 foi 6,5 vezes mais rápida que o M4 Air em geração de stream único, 2,1 vezes mais rápida que o Mac Studio com M4 Max e 1,2 vez mais rápida que o MacBook Pro com M5 Max
- Com um prompt de 4 mil tokens, o M4 MacBook Air levou 17 segundos no prefill, mas o mesmo M4 Air com eGPU caiu para 150 ms, ficando 120 vezes mais rápido
- Ao aumentar as requisições simultâneas de 1 para 4, a vazão total da configuração com RTX 5090 subiu cerca de 3 vezes, enquanto os Macs com Apple Silicon escalaram menos
-
Gemma 4
- Gemma 4 31B é um modelo denso de 31B, não um MoE esparso, então todo token passa por todos os parâmetros
- A GPU integrada do M4 Air não passou de 2 a 3 tokens por segundo nos testes, ficando fora de uma faixa útil
- As configurações com RTX 5090 baseadas em vLLM ficaram todas em cerca de 50 t/s, com diferença de apenas alguns pontos percentuais, enquanto o Mac Studio com M4 Max fez cerca de 22 t/s e o MacBook Pro com M5 Max cerca de 27 t/s
- No prefill, a RTX 5090 sempre ficou abaixo de 400 ms, enquanto o M4 Max levou até 21 segundos para processar um prompt de 4 mil tokens e o M5 Max cerca de 7,5 segundos
- As configurações com vLLM alcançaram cerca de 3,5 vezes a vazão com 4 requisições simultâneas, enquanto o backend MLX no Mac praticamente não cresceu nesse cenário
Possibilidade de uso direto e estabilidade
-
Distribuição e requisitos de build
- No estado atual, ainda não é algo no estilo “baixar e sair usando”, porque exige um entitlement especial da Apple
- Esse entitlement já foi solicitado, mas ainda não houve resposta, e a espera pode levar meses
- Enquanto isso, ainda é possível compilar o driver manualmente, desde que essa conta de certificado de assinatura inclua o Mac em questão
- Não é necessário desativar o SIP nem usar o modo de segurança reduzida
- O código está disponível em qemu-vfio-apple
- O launcher incluído baixa automaticamente uma imagem Ubuntu pré-compilada com o driver especial
apple_dmajá instalado
-
Estabilidade
- A estabilidade ainda não é boa
- O FEX atualmente tem um bug em que o Steam trava com frequência em loop, e nessa configuração isso parece ainda pior
- Alguns jogos podem levar vários minutos para iniciar
- Por causa do limite de mapeamento DMA, os mapeamentos podem se fragmentar com o tempo, deixando de haver espaço para iniciar um novo jogo
- Nesses casos, é preciso desligar a VM Linux e desconectar e reconectar a GPU para limpar todos os mapeamentos DMA antes de tentar de novo
- A carga mais estável é IA, e para esse uso o sistema funciona muito bem
- O trabalho de integração com o QEMU upstream e dos patches segue em andamento, e o objetivo ideal é chegar a distribuições principais de QEMU, como o UTM, funcionando de forma pronta para uso
1 comentários
Comentários do Hacker News
Há anos venho pedindo à equipe de VM pass-through de GPU para VM. Trabalhei no Apple Silicon Mac Pro, e teria feito muito mais sentido se fosse possível fazer pass-through da GPU que fica dentro do gabinete para uma VM Linux
Infelizmente o pedido não foi aceito, mas é legal ver outras pessoas conseguindo fazer isso funcionar
No fim, parece ser só uma questão de um monitor de máquina virtual como o QEMU adotar essa interface além de algo como o Linux VFIO. Se estiver falando do Virtualization.framework, ele em si já é praticamente um tipo de monitor de máquina virtual, então fico curioso sobre o que exatamente estaria faltando no macOS
Recentemente também entrou no QEMU mainline um patch para usar um "venus server" junto com virtio-gpu para suportar algo parecido em guests Linux sob o Hypervisor.framework. Segundo, aparentemente existe suporte interno na Apple para pass-through de PCI no Virtualization.framework. O código do framework em si é distribuído aos clientes, mas parece depender de kexts ou componentes de kernel que não vêm no macOS comum. Não sei se existe intenção de abrir isso para clientes, mas é claro que alguém na Apple pensou nessa funcionalidade
De qualquer forma, o Mac Pro agora é um produto morto. Só vender para profissionais de áudio e vídeo tem limite
Excelente texto. Os benchmarks de jogos são divertidos, mas na prática a parte realmente interessante é a melhora de desempenho em LLM
A plataforma da Apple é um ambiente acessível e fácil para rodar modelos locais por causa da RAM abundante, mas a velocidade relativamente lenta de processamento de prompt costuma ser ignorada. Como diz a citação do texto, com um prompt de 4K tokens o M4 MacBook Air leva 17 segundos para fazer o parsing antes de começar a gerar resposta, enquanto com uma eGPU termina em 150 ms, ou seja, 120 vezes mais rápido. Quando se mexe com LLM em chats pequenos, o problema de prefill quase não aparece, mas quando você começa a usar para tarefas maiores, o limite de computação vira gargalo. O gráfico de tempo até o primeiro token (TTFT) também não parece ruim à primeira vista, mas ganha outro significado quando se vê que a plataforma Mac era tão mais lenta do que o total de computação da GPU que precisaram usar escala logarítmica
Quando o autor apresenta os resultados desse jeito, pode passar uma impressão de viés, embora eu acredite que não seja o caso
Parece correto dizer que OpenGL já não é bem suportado no macOS, então o jogo fica completamente injogável mesmo com CrossOver
Mas o Doom aparentemente suporta Vulkan, e talvez dê para resolver adicionando
VK_NV_glsl_shaderao MoltenVK. Ainda parece muito menos trabalho do que pendurar uma RTX 5090 num M4, mas mesmo assim, aplausos para o Scott. A velocidade de inferência local de IA também é bem legal, é realmente um projeto malucoBem impressionante. Minha impressão era de que eGPU simplesmente não funcionava no Apple Silicon
A Apple também diz isso. “Para usar uma eGPU, é necessário um Mac com processador Intel.” Além disso, todas as eGPUs oficialmente suportadas eram AMD, não NVIDIA. https://support.apple.com/en-us/102363
Aqui a ideia é tunelar essa eGPU para uma VM Linux
No começo achei que seria um texto sobre rodar uma VM com um driver lento do tinygrad, mas na prática era uma abordagem muito melhor
É uma pena que a Apple não dê mais suporte e não flexibilize o limite de janela de 1,5 GB; isso deixaria tudo muito mais fácil. Arm no geral tem várias peculiaridades envolvendo dispositivos PCIe, mas pelo menos no Linux a maioria dos drivers modernos trata arm64 como cidadão de primeira classe, então ficou bem mais fácil
Meu palpite é que um grande motivo para o tinygrad ser lento é que o motor de inferência do tinygrad ainda não está muito otimizado para esses modelos públicos de LLM. Provavelmente a maior parte do trabalho foi para otimizar a stack da empresa de hardware de direção autônoma do George. Como não dá para simplesmente reaproveitar os kernels CUDA existentes naquele motor, a dificuldade de engenharia sobe bastante. Também fico curioso se meu projeto poderia compartilhar o driver de host macOS com o deles. Algumas mudanças seriam necessárias, mas parece haver bastante sobreposição
A parte de que “o primeiro passo da maioria dos projetos hoje em dia, queira admitir ou não, é perguntar para a IA” provavelmente significa, de forma mais realista, que a IA vai te dizer algo que ela mesma não sabe
Isso me lembrou de ontem, quando discuti com o ChatGPT se a 5070TI era uma placa de vídeo real. O ChatGPT insistia em me corrigir, dizendo que essa 5070TI não existia e que eu certamente queria dizer 4070ti
Pedi ao Claude para criar uma página HTML sobre o PowerShell 7, e ele escreveu que a 7.4 era a versão LTS mais recente. Eu passei um link mostrando que a 7.6 tinha sido lançada em março e pedi para refazer com a informação atualizada, mas ele gerou quase a mesma página e repetiu a mesma afirmação de que a 7.4 era a versão mais recente
Ainda assim, a resposta do ChatGPT ao post original estava exatamente certa. O resumo com “muito profundo”, “quase impraticável na prática” e “sob a ótica de pesquisa” descreve perfeitamente este texto
Isso é realmente impressionante. Tenho uma 5090 sobrando e estou rodando claw-like num M4 Mini; se eu colocar isso em algo como uma estrutura impressa em 3D e ligar à porta TB, pode acabar virando uma ferramenta bem útil para inferência local
Seria preciso algum dispositivo que garantisse de forma limpa coisas como alimentação elétrica. O problema é que
max-num-seqsemax-model-lenentram em conflito, e fora de um modo puramente de cliente único você acaba precisando, por assim dizer, de vários slotsIsso parece bem útil para inferência de IA, se conseguir passar pela aprovação da Apple. Eu queria usar uma GPU NVIDIA num Mac Mini, e desse jeito daria para rodar CUDA diretamente. Muito legal