Falta de desempenho de IA em PCs com IA: CPU supera NPU
(github.com/usefulsensors)- Ao executar um benchmark de NPU no SoC baseado em Qualcomm Arm do Microsoft Surface Pro 11th Edition, a vazão medida ficou muito abaixo do desempenho de aceleração promovido pelos Windows AI PCs
- O teste foi estruturado usando o Onnx Runtime e o provedor de execução Qualcomm QNN para executar uma operação MatMul grande, semelhante a uma camada custosa de modelos da família transformer
- Nos resultados de exemplo, a CPU registrou 821 Gigaops/s; a NPU registrou 225 Gigaops/s na configuração de quantização com entradas e saídas em float, e 573 Gigaops/s na configuração com entradas e saídas também em 8 bits
- Os 573 Gigaops/s da NPU correspondem a cerca de 1,3% dos 45 Teraops/s divulgados nos materiais de marketing do Microsoft Surface Pro 11th Edition; ao executar o mesmo modelo em uma Nvidia Geforce RTX 4080 Laptop GPU, o resultado é 3,2 ms e 2.160 Gigaops/s
- Os resultados atuais são de 2 de outubro de 2024; embora se espere que melhorias em software, frameworks e drivers possam reduzir a latência, neste benchmark a CPU é mais rápida que a NPU
Objetivo do benchmark da NPU Qualcomm no Surface
- A Microsoft vende o tablet Surface com SoC baseado em Qualcomm Arm como um Windows AI PC, promovendo a capacidade de executar modelos de machine learning de forma mais rápida e eficiente
- A Useful Sensors vê o hardware da Qualcomm e a NPU de forma positiva, e investiu tempo e recursos para portar seus aplicativos de terceiros para essa plataforma
- Como não há muitos exemplos de código ou benchmarks mostrando como desenvolvedores externos podem obter resultados rápidos, a empresa publicou o desempenho observado na prática como um pequeno projeto independente
- O desempenho medido ficou muito abaixo do esperado; como há experiência de que o mesmo hardware funciona de forma eficaz em outras plataformas, como Android, fica aberta a possibilidade de melhorias futuras por meio de mudanças em aplicativos, frameworks e drivers
Ambiente de execução e restrições de instalação
- O teste usa um script Python no Windows
- Em 2 de outubro de 2024, o Python da Microsoft Store não oferece suporte à arquitetura Arm, portanto não é adequado para executar os pacotes necessários para acessar a NPU da Qualcomm
- Nos resultados, foi usado o instalador Arm64 do Python 3.11.9
- Como ainda não há pacote Onnx pré-compilado para Windows on Arm, são necessários CMake e o compilador do Visual Studio
- O CMake é instalado com
winget install cmake - O Visual Studio usado é o Visual Studio Community Edition; durante a instalação, selecione a carga de trabalho
Desktop C++ Development
- O CMake é instalado com
- Os pacotes Python são instalados a partir da pasta do repositório com
py -m pip install -r requirements.txt- O branch do Onnx é uma versão em que a correção de compilação do launcher oficial
pyfoi retroportada para o Onnx 1.16 - Essa combinação é usada porque o Qualcomm Onnx Runtime apresenta o erro
Unsupported model IR versioncom versões mais recentes do Onnx - Para o pacote Qualcomm Onnx Runtime, foi usado um build nightly
- O branch do Onnx é uma versão em que a correção de compilação do launcher oficial
Execução do benchmark e interpretação da saída
- O benchmark é executado com
py benchmark_matmul.py - O Onnx Runtime imprime muitos logs durante a execução inicial
- Por exemplo, aparece uma mensagem de
cpuinfodizendo que o modelo do chipSnapdragon(R) X 12-core X1E80100 @ 3.40 GHznão é conhecido - Também são exibidos logs das etapas de finalização e conclusão do grafo
- Por exemplo, aparece uma mensagem de
- Os resultados reais do benchmark aparecem no final
NPU quantized compute, float I/O accuracy difference is 0.0100NPU quantized compute and I/O accuracy difference is 0.0060CPU took 8.42ms, 821,141,860,688 ops per secondNPU (quantized compute, float I/O) took 30.63ms, 225,667,671,183 ops per secondNPU (quantized compute and I/O) took 12.05ms, 573,475,650,364 ops per second
- As duas primeiras linhas mostram a diferença de precisão, verificando se os resultados numéricos da CPU e da NPU coincidem entre si
- As três últimas linhas mostram o tempo de relógio de parede para executar o modelo de ponta a ponta e a quantidade de operações por segundo calculada a partir dessa latência
Modelo medido e método de quantização
- O benchmark foi projetado para reproduzir seis grandes multiplicações de matrizes, semelhantes a camadas que consomem muito tempo em modelos transformer como o OpenAI Whisper
- O formato da entrada é
(6, 1500, 256) X (6, 256, 1500) - O formato do resultado é
(6, 1500, 1500) - O modelo consiste em um único nó
MatMul, com duas entradas e uma saída
- O formato da entrada é
- O modelo é gerado dinamicamente com o framework de modelos Onnx e então entregue ao Onnx Runtime
- O modelo de referência é uma versão puramente float e é executado apenas na CPU
- Para rodar com eficiência na NPU, geralmente é necessário usar principalmente modelos quantizados; o suporte a float16 é limitado
- A primeira abordagem para a NPU usa o método oficial ORT
quantize_static()- Por conveniência, os tensores de entrada e saída permanecem em float de 32 bits
- As conversões em tempo de execução são feitas no início e no fim do grafo, enquanto o restante da computação roda em 8 bits
- Nessa configuração, as operações de conversão da NPU foram muito lentas, e em
npu_quant_profile.csva conversão respondeu por mais de 75% do tempo total - A segunda abordagem constrói programaticamente um grafo de modelo equivalente com entradas e saídas em 8 bits
- Esse método de
quantized compute and I/Ocostuma ser cerca de 3 vezes mais rápido que a versão com I/O em float - No profiling, a maior parte do tempo é gasta, como esperado, na multiplicação de matrizes
- Esse método de
Variáveis consideradas na medição de desempenho
- A possibilidade de ser compute bound foi considerada, ajustando o formato das matrizes para algo mais próximo de quadrado
- Modelos transformer modernos, diferentemente de modelos convolution mais antigos, baseiam-se em grandes multiplicações de matrizes
- Quando uma camada se aproxima de uma multiplicação matriz-vetor, há menos reutilização de pesos e a busca de valores na DRAM pode se tornar o gargalo
- A dimensão
kda matriz original do tiny Whisper era 64, mas neste benchmark foi aumentada para 256 para ampliar o espaço para otimizações SIMD
- As configurações de energia foram ajustadas para favorecer desempenho
- A intenção era deixar todas as configurações de energia do Windows em
Best Performance - O benchmark foi executado com o tablet conectado à tomada
- A opção de sessão
htp_performance_modedo Qualcomm Onnx Runtime foi definida comosustained_high_performance, que apresentou a menor latência total nos experimentos
- A intenção era deixar todas as configurações de energia do Windows em
- A estrutura do modelo foi limitada a uma única multiplicação de matrizes para facilitar a interpretação
- Múltiplas camadas, convolution e pesos estáticos também seriam possíveis, mas foi escolhido um único MatMul com entradas dinâmicas para refletir a estrutura transformer amplamente usada em LLMs e modelos modernos
- Ainda existe a possibilidade de erro de configuração
- São usados quantização unsigned de 8 bits e elementos
qdqdentro do grafo - Houve a tentativa de seguir as melhores práticas da documentação, mas é possível que a configuração tenha ficado fora do caminho rápido da implementação do driver ou do acelerador
- São usados quantização unsigned de 8 bits e elementos
- Também foram analisadas opções de API para acessar aceleração de IA no Windows
- O DirectML parece oferecer suporte apenas ao acesso à GPU
- O OpenVino parece não rodar nesse hardware Arm
- O uso direto do Qualcomm QNN SDK também apresentou resultados de desempenho semelhantes
- O TensorFlow Lite não oferece suporte ao Windows for Arm
- Nesta investigação e nos experimentos, o Onnx parece ser o framework mais adequado para obter desempenho de aceleração na NPU, com suporte tanto da Microsoft quanto da Qualcomm
Interpretação dos resultados
- Os resultados são de 2 de outubro de 2024 e foram medidos no Microsoft Surface Pro 11th Edition
- O SoC é o
Snapdragon(R) X 12-core X1E80100 @ 3.40 GHz
- O SoC é o
- Mesmo excluindo a conversão de float, o resultado da NPU é mais lento que o da CPU
- Do ponto de vista de um acelerador, isso não é ideal
- Ainda assim, não se descarta a possibilidade de vantagens em eficiência energética ou desempenho sustentado
- O maior desempenho medido da NPU, 573 billion ops/s, equivale a 1,3% dos 45 trillion ops/s dos materiais de marketing do Microsoft Surface Pro 11th Edition
- Ao executar o mesmo modelo em uma Nvidia Geforce RTX 4080 Laptop GPU, o tempo é de 3,2 ms
- Isso corresponde a 2.160 billion ops/s
- É uma vazão quase 4 vezes maior que a medida na NPU do Surface
2 comentários
Eu achava que a NPU da Ryzen era só um pouco mais rápida que a CPU, mas meu entusiasmo pelo Snapdragon caiu bastante.
Opiniões do Hacker News
Pelos resultados, no geral parece que os recursos de computação não foram bem aproveitados. CPU em 8,4 ms e GPU em 3,2 ms é uma diferença pequena demais; aqui seria razoável esperar algo como 10 a 20 vezes de diferença.
A causa pode ser o onnxruntime. Parece haver casos em que alguns fornecedores de hardware lançam apenas as unidades de computação, mas ainda não oferecem suporte adequado; resta ver quão rápido isso vai mudar.
Além disso, muita gente entende errado o objetivo de uma NPU como sendo “velocidade”, mas o ponto principal é baixo consumo de energia. Para buscar velocidade, é preciso eliminar o gargalo de memória e, no fim, projetar um ASIC com memória própria. Na maioria dos dispositivos, a NPU fica acoplada ao SoC ao redor da CPU para descarregar operações de IA.
Seria interessante rodar este benchmark em loop infinito nos três dispositivos — CPU/NPU/GPU — e medir o consumo de energia. Eu esperaria que a NPU fosse a mais econômica e também tivesse a melhor computação por watt.
Por exemplo, em um filtro de foto com IA, é bem provável que a GPU esteja renderizando a pré-visualização, enquanto a CPU está ocupada tratando a UI e a entrada do usuário.
O Apple Accelerate era, na prática, o único caminho para acessar as unidades de matriz da CPU antes do M4 e a NPU. Se você quiser usar outra coisa, precisa estar preparado para enviar patches ao framework de machine learning escolhido, ou para escrever você mesmo o código de treinamento e inferência.
No fim, o Python também chama C, mas é bem interessante ver quanto desempenho desaparece.
Se estiver usando GPU, é preciso considerar se o timing inclui operações assíncronas.
Se você usar
time.time()de forma ingênua, a CPU apenas registra o tempo, emodel(input.cuda()).cuda()envia os dados para a memória da GPU e inicia a computação, mas como isso é assíncrono, o tempo de término pode ser registrado independentemente de o resultado estar realmente pronto.É um comportamento difícil de esperar sem conhecer o sistema e o hardware. Não é só Python: a maioria das linguagens é projetada para compilar para uma forma mais otimizada do que o código escrito, e, como não há bloqueio, isso não impede o trabalho da CPU.
Para medir de fato o trabalho na GPU, é preciso olhar para os temporizadores de eventos CUDA. No PyTorch, a forma é usar
torch.cuda.Event(enable_timing=True).Além disso, o tamanho e o formato da memória também são complicados. Este benchmark usa um formato desfavorável para a NPU. NPUs e GPUs geralmente querem channels last, então
[1,1500,1500,6]é mais adequado do que[1,6,1500,1500].1500 e 6 também são números estranhos e não são bons para a NPU; considerando que esses dispositivos ainda são novos, a perda de desempenho pode ser bem grande.
Escrevi mais detalhes em https://news.ycombinator.com/item?id=41864828
Essas NPUs ocupam uma área de silício considerável, então seria uma pena mesmo se acabassem sendo pouco usadas. Não encontrei uma análise de die separando só a NPU do Snapdragon X, mas no caso da AMD, com uma meta parecida de cerca de 50 TOPS, dá para ver aqui, e ela ocupa uma área equivalente a mais ou menos 3 núcleos de CPU de alto desempenho.
https://www.techpowerup.com/325035/amd-strix-point-silicon-p...
Pior ainda, parece desnecessário para a maioria das pessoas, e pesquisas recentes até mostram predominância de rejeição à penetração da IA. Não deveríamos pagar a mais por esse tipo de coisa; deveria ser opcional.
Assim, as vendas mostrariam quão poucas pessoas querem pagar o prêmio por “IA”, deixando claro o quanto isso é exagerado e desnecessário.
Portanto, o objetivo dessas peças é serem usadas, mas não usadas demais.
Em vez da NPU, esses transistores e espaço de die poderiam ter sido usados para várias finalidades, mas provavelmente não para adicionar mais núcleos de CPU de alto desempenho. Isso deixaria a densidade de potência alta demais e criaria problemas térmicos difíceis de resolver sem throttling permanente.
[1] https://en.wikipedia.org/wiki/Dark_silicon
Para quem não vai rodar modelos, parece desperdício, e fico curioso sobre que outras formas existem de usá-la.
Software de consumo não escala até esse ponto, então fico pensando o que se poderia fazer alocando mais transistores à CPU.
É parecido com o motivo de a Apple colocar tantos motores de vídeo no SoC. Com o orçamento de transistores disponível, não há muitos outros lugares úteis para empregá-los. Melhorias de desempenho single-thread não são mais limitadas apenas pela contagem de transistores, e o software não lida bem com multithreading.
Eu achava que o objetivo desses dispositivos não era serem rápidos, mas rodar modelos pequenos com consumo de energia muito baixo. Uso um notebook AMD recente com NPU e, mesmo ativando um efeito de vídeo que roda na NPU, o consumo de energia não muda; já com o Nvidia Studio Effects, o consumo sobe
A NPU parece ser voltada a modelos muito otimizados que fazem tarefas pequenas, como correção de olhar, desfoque de fundo, modelos de correção automática, transcrição e OCR. Especialmente no Windows, entendi que ela roda OCR da tela inteira e embeddings para busca por causa do recurso de rewind
A AMD vem fazendo um ótimo trabalho ultimamente, mas parece não alardear muito isso. Isto é particularmente interessante: https://lore.kernel.org/lkml/DM6PR12MB3993D5ECA50B27682AEBE1...
Edit: não era um FPGA. Aprendi hoje
Dá para confirmar isso ao avaliar modelos CoreML no macOS. O ANE leva mais ou menos metade do tempo da GPU, e a GPU leva mais ou menos metade do tempo da CPU. O multiplicador real varia conforme o modelo
O chip que ameaçará a Nvidia será um chip e memória baratos o suficiente para rodar modelos bons o bastante em dispositivos pessoais como smartphones
Se o público em geral concordar com a utilidade dos LLMs e estiver disposto a pagar um pequeno prêmio no preço do aparelho, vejo o futuro dessa tecnologia como modelos pessoais que oferecem privacidade por natureza
A quantidade de informações pessoais que as pessoas despejam em lugares como o ChatGPT é impressionante. No Reddit, parece comum ver viciados em apps de namorada virtual com IA entregando seus gostos mais sombrios, confissões vulneráveis e até conversas potencialmente criminosas a empresas de apps sem nome
O Google também deixa claro que, se o histórico do Gemini estiver ativado, pode revisar o conteúdo das conversas
Previsões complexas de tokens que exijam modelos maiores podem até ser perguntadas a LLMs na nuvem, mas, para consumidores, a privacidade precisa ser garantida
Para assistentes pessoais cotidianos, chat e busca de informações, não vejo necessidade de raciocínio de ponta nem de LLMs acrobáticos
Ainda assim, desconfio um pouco das conclusões específicas do texto. É ONNX da Qualcomm, e pode estar antigo. No lado Android, era comum criticar bastante a engenharia de software da Qualcomm
Mesmo assim, a direção está correta. A maioria das alegações de aceleração de IA em hardware de consumidor é quase universalmente exagero, com exceções quando A) você usa software 1P ou B) alguém internamente no 1P realmente quer que você aproveite aquele recurso
Mesmo usando algo como TensorRT, não será tão rápido quanto escrever tudo manualmente do zero, e há um motivo para a Nvidia colocar tanta gente nisso. Ainda assim, chega bem perto e reduz muito o tempo de desenvolvimento
Esses dispositivos, em geral, são otimizados para tarefas semelhantes e repetitivas. Por isso, acho que parte das informações coletadas aqui pode estar incorreta
Não usei esses chips NPU diretamente, mas é difícil confiar nas medições de tempo. A medição CUDA no fim, pelo código, muito provavelmente não foi feita corretamente. Medir tempo é mais difícil do que parece
O número de operações anunciado conta apenas as operações executadas diretamente na NPU, enquanto o texto original pode ter incluído trabalho da CPU nas medições da NPU e da GPU. A documentação tem uma ferramenta de benchmark, então imagino que tenham usado algo parecido, e também fiquei curioso sobre a variância depois do aquecimento
O formato dos dados também parece errado. Aqui é necessário
channels last. A documentação também confirma issoO número 1500 também soa estranho, então pode introduzir outros problemas. Com 1536, 2048, 256 ou valores menores, os resultados poderiam ser diferentes. Modelos reais não processam imagens em resolução total, e, se a arquitetura for otimizada para o modelo, as informações de forma passam a importar. Em machine learning, otimização de formas é bastante importante
Dando uma olhada rápida na documentação, a configuração também parece inadequada. Em “Model Workflow”, eles dizem que querem os dados em ponto flutuante de 8 ou 16 bits, mas há vários tipos de ponto flutuante. O bfloat do PyTorch não é o mesmo que
torch.halfoutorch.float16Precisão mista ainda é um tema confuso, então, quando há esse tipo de problema, vale a pena investigar direito. Não recomendo simplesmente rodar um procedimento padrão de quantização e parar por aí. É um bom ponto de partida, mas, se “bom o suficiente” não for suficiente, não se deve parar ali
Ainda assim, não acho que esses resultados sejam inúteis. Só precisam ser melhorados. Esse tipo de trabalho é mais complexo do que parece, e boa parte disso se deve ao fato de a tecnologia ser nova e os detalhes ainda estarem sendo definidos
Ao comparar com CPU ou GPU, especialmente CUDA, é preciso lembrar que centenas de milhares de horas-pessoa foram investidas, e até bibliotecas de alto nível como Python receberam pelo menos dezenas de milhares de horas-pessoa. Esses dispositivos ainda não estão prontos para uso direto no nível de abstração de linguagem que o usuário médio prefere, mas são bastante úteis se você estiver disposto a trabalhar mais perto do hardware
Para medir operações assíncronas de GPU no PyTorch, é preciso usar eventos CUDA e
torch.cuda.synchronize(), em vez de envolver a saída do modelo com um temporizador de CPU[1] https://www.thonking.ai/p/what-shapes-do-matrix-multiplicati...
Para implantar modelos em uma NPU, é necessária uma quantidade considerável de otimização baseada em perfil. Se você pegar um modelo que roda bem na CPU e o levar para a NPU sem otimizá-lo para ela, normalmente o resultado será decepcionante
A descrição do repositório no GitHub é muito mais informativa que o blog
Rodando multiplicação de matrizes int8 com onnx, o desempenho é de cerca de 0,6 TF
https://github.com/usefulsensors/qc_npu_benchmark
Dizem que tornaram as matrizes de entrada mais próximas de quadradas para permitir tiling e reutilização, mas não me surpreenderia se muitas das otimizações possíveis não tivessem entrado no Onnx
A Qualcomm não dá acesso direto à NPU; parece esperar que o usuário converta o modelo com um framework e entregue a ela. Pela minha experiência, ferramentas de conversão em geral são ruins e deixam muita otimização passar
Portanto, talvez não seja que “a NPU seja ruim”, mas sim que “a ferramenta de conversão seja ruim”. Vou esperar até haver acesso direto, e não confio nas ferramentas de conversão
Vejo a NPU como boa para modelos de machine learning muito pequenos e aproximação de funções muito rápida. Esse é o uso que eu tinha em mente. LLMs estão em alta hoje, mas há uma quantidade enorme de tarefas especializadas em que modelos pequenos são realmente úteis
Não estou dizendo que isso não exista, mas sinceramente não sei quais seriam e gostaria de saber
Espero que a NPU do Elite X tenha ficado mais fácil de acessar por causa do Copilot+, mas o ponto é que não há como ser tão simples quanto “se eu executar um modelo genérico, ele vai se teletransportar magicamente para a NPU”
A RTX 4080 deveria conseguir cerca de 40 TFLOPS, mas aqui eles relatam apenas 216 bilhões de operações por segundo. Acho que isso já é motivo para revisar o benchmark
É muito provável que tenha havido um erro sério na medição de FLOPS. É possível a CPU vencer a NPU, mas, para uma comparação correta, seria preciso fazer benchmark de várias multiplicações de matrizes sem sincronização da aplicação
O benchmark é uma multiplicação de matrizes no formato
(6, 1500, 256) X (6, 256, 1500), o que não é um tamanho muito grande no mundo de IA. Com matrizes muito maiores, a diferença ficaria ainda maiorPor exemplo, até o Llama 3.1 8B, que é um dos modelos pequenos, tem multiplicações de matrizes como
(batch, 14336, 4096) x (batch, 4096, 14336)Acho que este benchmark não é realista o suficiente
Rodei o qprof, profiler da NPU da Qualcomm, neste benchmark. Pelos resultados do perfil, a tarefa foi alocada aos núcleos vetoriais, não aos tensor cores, que fornecem a maior parte da capacidade computacional da NPU
Fazendo uma conta aproximada, o HMX parece ser 30 vezes mais potente que o HVX
Como a carga de trabalho é relativamente pequena, o hardware não é plenamente utilizado por causa do overhead de quantização/desquantização de entrada/saída e do mapeamento NCHW-NHCW. Preencher os pesos e as entradas para múltiplos de 64 também ajudaria no desempenho
Gráfico de profiling: https://imgur.com/a/2OKR93e
A capacidade de computação HVX estimada é
4 * 2 * 1.43 * 1024 / 8 = 1.46TOPSem int8. Aqui, 4 é o número de núcleos vetoriais, 2 é o número de operações por ciclo, 1,43 GHz é a frequência do HVX, 1024 bits é a largura do registrador vetorial e 8 bits é a precisão4 * 2 * 1.43 * 1024 / 8O título real do artigo deveria ser “Benchmarking Qualcomm's NPU on the Microsoft Surface Tablet”
Isto não é um texto sobre NPUs em geral, mas sobre uma NPU específica analisada com um benchmark específico e uma combinação específica de biblioteca/framework. Então, na prática, não prova nada