Falta de rendimiento de IA en las AI PC: la CPU supera a la NPU
(github.com/usefulsensors)- Al ejecutar benchmarks de NPU en el SoC basado en Qualcomm Arm de la Microsoft Surface Pro 11th Edition, se midió un rendimiento muy inferior al que prometen las Windows AI PC en aceleración
- La prueba se configuró para ejecutar operaciones MatMul grandes similares a las capas más costosas de modelos de la familia transformer, usando Onnx Runtime y el proveedor de ejecución Qualcomm QNN
- En los resultados de ejemplo, la CPU registró 821 Gigaops/s, mientras que la NPU obtuvo 225 Gigaops/s en una configuración con cuantización y E/S float, y 573 Gigaops/s en una configuración con E/S de 8 bits
- Los 573 Gigaops/s de la NPU equivalen al 1.3% de los 45 Teraops/s del material de marketing de la Microsoft Surface Pro 11th Edition; al ejecutar el mismo modelo en una Nvidia Geforce RTX 4080 Laptop GPU se obtienen 3.2 ms y 2,160 Gigaops/s
- Estos resultados corresponden al 2 de octubre de 2024; aunque se espera que mejoras en software, frameworks y drivers reduzcan la latencia, en este benchmark actual la CPU es más rápida que la NPU
Objetivo del benchmark de la NPU de Surface
- Microsoft vende las tablets Surface con SoC basados en Qualcomm Arm como AI PC con Windows, promocionando que pueden ejecutar modelos de machine learning de forma más rápida y eficiente
- Useful Sensors tiene una visión positiva del hardware de Qualcomm y de su NPU, y dedicó tiempo y recursos a portar su app de terceros a esta plataforma
- Como no hay muchos ejemplos de código ni benchmarks que muestren cómo un desarrollador externo puede obtener resultados rápidos, publicaron el rendimiento observado en un pequeño proyecto independiente
- El rendimiento medido fue mucho menor de lo esperado, y como en otras plataformas como Android el mismo hardware ha funcionado bien, dejan abierta la posibilidad de mejora mediante cambios en aplicaciones, frameworks o drivers
Entorno de ejecución y limitaciones de instalación
- La prueba usa scripts de Python en Windows
- Al 2 de octubre de 2024, Python de Microsoft Store no soporta la arquitectura Arm, por lo que no sirve para ejecutar los paquetes necesarios para acceder a la NPU de Qualcomm
- En los resultados se utilizó el instalador de Python 3.11.9 Arm64
- Como todavía no hay paquetes precompilados de Onnx para Windows on Arm, se requieren CMake y el compilador de Visual Studio
- CMake se instala con
winget install cmake - Para Visual Studio se usa Visual Studio Community Edition, seleccionando la carga de trabajo
Desktop C++ Developmentdurante la instalación
- CMake se instala con
- Los paquetes de Python se instalan desde la carpeta del repositorio con
py -m pip install -r requirements.txt- La rama de Onnx es una versión en la que se retroportó a Onnx 1.16 una corrección de compilación para el lanzador oficial
py - Se usa esta combinación porque Qualcomm Onnx Runtime arroja el error
Unsupported model IR versioncon las versiones más recientes de Onnx - Se utilizó una nightly build del paquete Qualcomm Onnx Runtime
- La rama de Onnx es una versión en la que se retroportó a Onnx 1.16 una corrección de compilación para el lanzador oficial
Ejecución del benchmark e interpretación de la salida
- El benchmark se ejecuta con
py benchmark_matmul.py - Onnx Runtime imprime muchos logs durante la ejecución inicial
- Por ejemplo, aparece un mensaje de
cpuinfoindicando que no reconoce el modelo de chipSnapdragon(R) X 12-core X1E80100 @ 3.40 GHz - También se imprimen logs de la fase de finalización del grafo y de la fase de completado
- Por ejemplo, aparece un mensaje de
- Los resultados reales del benchmark aparecen al 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
- Las dos primeras líneas muestran la diferencia de precisión para verificar si los resultados numéricos de CPU y NPU coinciden entre sí
- Las últimas tres líneas muestran el tiempo de reloj total para ejecutar el modelo de principio a fin y el rendimiento en operaciones por segundo calculado a partir de esa latencia
Modelo medido y método de cuantización
- El benchmark está diseñado para reproducir 6 multiplicaciones de matrices grandes parecidas a las capas que más tiempo consumen en modelos transformer como OpenAI Whisper
- La forma de entrada es
(6, 1500, 256) X (6, 256, 1500) - La forma de salida es
(6, 1500, 1500) - El modelo consiste en un único nodo
MatMulcon 2 entradas y 1 salida
- La forma de entrada es
- El modelo se genera sobre la marcha con el framework de modelos Onnx y luego se pasa a Onnx Runtime
- El modelo base es una versión float pura y solo se ejecuta en la CPU
- Para ejecutarse de forma efectiva en la NPU, en general se requieren modelos cuantizados, y el soporte para float16 es limitado
- El primer enfoque para la NPU usa el método oficial
quantize_static()de ORT- Por conveniencia, los tensores de entrada y salida se dejaron como float de 32 bits
- Se hacen conversiones en tiempo de ejecución al inicio y al final del grafo, mientras que el resto del cálculo se ejecuta en 8 bits
- En esta configuración, las operaciones de conversión de la NPU fueron muy lentas, y en
npu_quant_profile.csvlas conversiones representaron más del 75% del tiempo total - El segundo enfoque construye por programa un grafo de modelo equivalente con entradas y salidas de 8 bits
- Este enfoque de
quantized compute and I/Osuele ser aproximadamente 3 veces más rápido que la versión con E/S float - En el profiling, la mayor parte del tiempo se dedica a la multiplicación de matrices, como era de esperarse
- Este enfoque de
Variables consideradas en la medición de rendimiento
- Se eligió una forma de matriz más cercana a un cuadrado para considerar si el caso era compute bound
- A diferencia de los modelos antiguos basados en convolución, los modelos transformer modernos se apoyan en multiplicaciones de matrices grandes
- Si una capa se acerca más a una multiplicación matriz-vector, disminuye la reutilización de pesos y traer valores desde DRAM puede convertirse en cuello de botella
- La dimensión
kde la matriz original de tiny Whisper era 64, pero en este benchmark se subió a 256 para ampliar el margen de optimización SIMD
- La configuración de energía se ajustó para maximizar el rendimiento
- Se intentó dejar toda la configuración de energía de Windows en
Best Performance - El benchmark se ejecutó con la tablet conectada a la corriente
- La opción de sesión
htp_performance_modede Qualcomm Onnx Runtime se configuró ensustained_high_performance, que en las pruebas dio la menor latencia total
- Se intentó dejar toda la configuración de energía de Windows en
- La estructura del modelo se limitó a una sola multiplicación de matrices para facilitar la interpretación
- Era posible usar varias capas, convolución y pesos estáticos, pero se eligió un único MatMul con entradas dinámicas para reflejar la estructura transformer ampliamente usada en LLM y modelos modernos
- También queda abierta la posibilidad de errores de configuración
- Se usa cuantización unsigned de 8 bits y elementos
qdqdentro del grafo - Aunque se intentó seguir las mejores prácticas de la documentación, es posible haber quedado fuera de la ruta rápida del driver o de la implementación del acelerador
- Se usa cuantización unsigned de 8 bits y elementos
- También se revisaron las opciones de API para acceder a la aceleración de IA en Windows
- DirectML parece soportar solo acceso a GPU
- OpenVino parece no ejecutarse en este hardware Arm
- Incluso usando directamente el Qualcomm QNN SDK, se observaron resultados de rendimiento similares
- TensorFlow Lite no soporta Windows for Arm
- En esta investigación y pruebas, Onnx parece ser el framework más adecuado para obtener aceleración por NPU, con soporte tanto de Microsoft como de Qualcomm
Interpretación de los resultados
- Los resultados corresponden al 2 de octubre de 2024 y fueron medidos en una Microsoft Surface Pro 11th Edition
- El SoC es
Snapdragon(R) X 12-core X1E80100 @ 3.40 GHz
- El SoC es
- Incluso excluyendo las conversiones float, el resultado de la NPU es más lento que el de la CPU
- Desde la perspectiva de un acelerador, esto no es ideal
- Aun así, no se descarta que pueda haber ventajas en eficiencia energética o en rendimiento sostenido
- El mejor rendimiento medido de la NPU, 573 billion ops/s, equivale al 1.3% de los 45 trillion ops/s del material de marketing de la Microsoft Surface Pro 11th Edition
- Al ejecutar el mismo modelo en una Nvidia Geforce RTX 4080 Laptop GPU, tarda 3.2 ms
- Eso equivale a 2,160 billion ops/s
- Es un rendimiento casi 4 veces mayor que la medición de la NPU de Surface
2 comentarios
Tenía entendido que el NPU de Ryzen era apenas un poco más rápido que la CPU, así que se me fueron por completo las ganas del Snapdragon.
Opiniones de Hacker News
Viendo los resultados, en general parece que no se están aprovechando bien los recursos de cómputo. Si son 8.4 ms en CPU y 3.2 ms en GPU, la diferencia es demasiado pequeña; aquí uno esperaría algo como 10 a 20 veces de diferencia.
La causa podría ser onnxruntime. Parece que algunos fabricantes de hardware sacan las unidades de cómputo, pero todavía no les agregan un soporte adecuado; habrá que ver qué tan rápido cambia eso.
Además, mucha gente malinterpreta el propósito de las NPU como “velocidad”, pero lo central es el bajo consumo. Si se busca velocidad, hay que eliminar el cuello de botella de memoria, y al final se termina diseñando un ASIC con memoria propia. En la mayoría de los dispositivos, la NPU está pegada al SoC alrededor de la CPU y sirve para descargar operaciones de IA.
Sería interesante ejecutar este benchmark en un loop infinito en los tres dispositivos —CPU/NPU/GPU— y medir el consumo eléctrico. Esperaría que la NPU sea la más baja y también la mejor en operaciones por watt.
Por ejemplo, en un filtro de fotos con IA, es muy probable que la GPU esté renderizando la vista previa y que la CPU esté ocupada con la UI y la entrada del usuario.
Apple Accelerate era prácticamente la única vía para acceder a las unidades matriciales de CPU anteriores al M4 y a la NPU. Si quieres usar otra cosa, tienes que estar listo para subir parches al framework de machine learning que hayas elegido, o para escribir tú mismo el código de entrenamiento e inferencia.
Al final Python también llama a C, pero es bastante interesante ver cuánto rendimiento se pierde.
Si se usa GPU, hay que considerar si el tiempo incluye trabajo asíncrono.
Si usas ingenuamente
time.time(), la CPU solo registra la hora, ymodel(input.cuda()).cuda()envía los datos a la memoria de la GPU e inicia el cálculo, pero como es asíncrono, puede registrar la hora de finalización sin importar si el resultado ya está realmente listo.Es un comportamiento difícil de anticipar si no conoces el sistema y el hardware. No solo Python: la mayoría de los lenguajes están diseñados para compilarse a una forma más optimizada que el código que escribiste, y como no hay bloqueo, no detienen el trabajo de la CPU.
Para medir de verdad el trabajo de GPU, hay que ver los temporizadores de eventos de CUDA. En PyTorch se usa algo como
torch.cuda.Event(enable_timing=True).Además, el tamaño y la forma de la memoria también son complicados. Este benchmark usa una forma desfavorable para la NPU. Las NPU y GPU normalmente quieren channels last, así que
[1,1500,1500,6]es más adecuado que[1,6,1500,1500].1500 y 6 también son números raros y no son buenos para la NPU; considerando que estos dispositivos todavía son nuevos, la pérdida de rendimiento puede ser bastante grande.
Dejé más detalles en https://news.ycombinator.com/item?id=41864828.
Estas NPU ocupan bastante área de silicio, así que sería una verdadera lástima si al final casi no se usan. No encontré un análisis de die que separe solo la NPU del Snapdragon X, pero del lado de AMD, con un objetivo parecido de unos 50 TOPS, se puede ver aquí, y ocupa un área equivalente a unos 3 núcleos de CPU de alto rendimiento.
https://www.techpowerup.com/325035/amd-strix-point-silicon-p...
Peor aún, tampoco parece que la mayoría de la gente lo necesite, y encuestas recientes incluso muestran que predomina el rechazo a la penetración de la IA. No deberíamos pagar un costo extra por esto; debería ser opcional.
Si fuera así, las ventas mostrarían cuán poca gente quiere pagar la prima de “IA”, y quedaría claro lo exagerado e innecesario que es.
Por eso, el propósito de estas piezas es que se usen, pero no demasiado.
En lugar de la NPU, se podrían haber usado esos transistores y espacio de die para varias cosas, pero no habrían agregado más núcleos de CPU de alto rendimiento. Eso elevaría demasiado la densidad de potencia y crearía un problema térmico difícil de resolver sin throttling permanente.
[1] https://en.wikipedia.org/wiki/Dark_silicon
Para quienes no van a correr modelos, parece un desperdicio, y me pregunto de qué formas podría usarse para otros fines.
El software de consumo no escala hasta ese nivel, así que me pregunto qué se podría hacer asignando más transistores a la CPU.
Es parecido a la razón por la que Apple pone tantos motores de video en sus SoC. Con el presupuesto de transistores disponible, no hay muchos otros lugares obvios donde usarlos. La mejora del rendimiento monohilo ya no está limitada solo por la cantidad de transistores, y el software no maneja bien el multithreading.
Pensé que el objetivo de estos dispositivos no era ser rápidos, sino ejecutar modelos pequeños con muy bajo consumo de energía. Uso una laptop AMD reciente con NPU, y aunque active efectos de video que supuestamente corren en la NPU, el consumo de energía no cambia; en cambio, si uso Nvidia Studio Effects, el consumo sube.
La NPU parece pensada para modelos muy optimizados que hacen tareas pequeñas, como contacto visual, desenfoque de fondo, modelos de autocorrección, transcripción y OCR. En especial en Windows, entiendo que ejecutan OCR de toda la pantalla y embeddings para búsqueda para la función de rewind.
AMD está haciendo un trabajo excelente últimamente, aunque parece que no lo promociona mucho. Esto es especialmente interesante: https://lore.kernel.org/lkml/DM6PR12MB3993D5ECA50B27682AEBE1...
Edición: no era un FPGA. Hoy aprendí algo.
Se puede comprobar evaluando modelos de CoreML en macOS. El ANE tarda más o menos la mitad que la GPU, y la GPU tarda más o menos la mitad que la CPU. El factor real depende del modelo.
El chip que amenace a Nvidia será uno lo bastante barato, junto con su memoria, para ejecutar modelos suficientemente buenos en dispositivos personales como smartphones.
Si el público general coincide en la utilidad de los LLM y está dispuesto a pagar una pequeña prima en el precio del dispositivo, creo que el futuro de esta tecnología está esencialmente en modelos personales que ofrezcan privacidad por diseño.
La cantidad de información personal que la gente vuelca en lugares como ChatGPT es sorprendente. Viendo Reddit, parece que los adictos a apps de novias virtuales con IA a menudo entregan a compañías de apps desconocidas sus gustos más oscuros, confesiones vulnerables e incluso conversaciones que podrían llegar a ser delictivas.
Google también deja claro que, si activas el historial de Gemini, pueden revisar el contenido de las conversaciones.
Para predicciones de tokens complejas que requieran modelos más grandes se podría consultar a un LLM en la nube, pero para el consumidor la privacidad debería estar garantizada sí o sí.
No creo que para un asistente personal cotidiano, chat o búsqueda de información hagan falta razonamientos de frontera ni LLMs acrobáticos.
Dicho eso, las conclusiones concretas del artículo me generan algunas dudas. Es ONNX de Qualcomm, y quizá esté desactualizado. En el lado de Android solíamos criticar mucho la ingeniería de software de Qualcomm.
Aun así, la dirección es correcta. La mayoría de las afirmaciones de aceleración de IA en hardware de consumo son casi universalmente humo, salvo cuando A) usas software 1P o B) alguien dentro del equipo 1P realmente quiere que aproveches esa función.
Incluso usando algo como TensorRT, no será tan rápido como escribirlo a mano desde cero, y hay una razón por la que Nvidia le dedica tanta gente. Aun así, se acerca bastante y reduce mucho el tiempo de escritura.
Estos dispositivos suelen estar optimizados para tareas repetitivas y parecidas. Por eso creo que parte de la información recopilada aquí puede ser inexacta.
No he usado directamente estos chips NPU, pero me cuesta confiar en las mediciones de tiempo. Es muy probable que el timing de CUDA al final no se haya medido correctamente en el código. Medir tiempos no es tan fácil como parece.
La cantidad de operaciones anunciada cuenta solo las operaciones ejecutadas directamente en la NPU, mientras que el post original quizá incluyó trabajo de CPU en las mediciones de NPU y GPU. La documentación tiene una herramienta de benchmarking, así que imagino que usaron un enfoque similar, y también me da curiosidad cómo se ve la varianza después del warm-up.
El formato de datos también parece incorrecto. Aquí se necesita
channels last. La documentación también lo confirma.El número 1500 también se ve raro, así que podría introducir errores adicionales. Con 1536, 2048, 256 o valores más pequeños, los resultados podrían cambiar. Los modelos reales no procesan imágenes a resolución completa, y si se optimiza la arquitectura para el modelo, la información de forma importa. En machine learning, la optimización de formas es bastante importante.
Mirando rápidamente la documentación, la configuración también parece inadecuada. En “Model Workflow” dicen que quieren los datos en coma flotante de 8 o 16 bits, pero hay varios tipos de coma flotante. El bfloat de PyTorch no es lo mismo que
torch.halfotorch.float16.La precisión mixta sigue siendo un tema confuso, así que si hay un problema de este tipo vale la pena revisarlo bien. No recomiendo simplemente ejecutar el procedimiento estándar de cuantización y darlo por terminado. Es un buen punto de partida, pero si no es “suficientemente bueno”, no deberías detenerte ahí.
Aun así, no creo que estos resultados sean inútiles. Solo necesitan mejoras. Este trabajo es más complejo de lo que parece, y buena parte de eso se debe a que la tecnología es nueva y los detalles todavía se están asentando.
Al comparar con CPU o GPU, especialmente CUDA, hay que recordar que se han invertido cientos de miles de horas-persona, y que incluso en bibliotecas de alto nivel como Python hay al menos decenas de miles de horas-persona. Estos dispositivos aún no están listos para usarse directamente al nivel de abstracción de lenguaje que prefiere el usuario promedio, pero si estás dispuesto a trabajar cerca del hardware pueden ser bastante útiles.
Para medir trabajo asíncrono de GPU en PyTorch, en vez de envolver la salida del modelo con un temporizador de CPU, hay que usar eventos CUDA y
torch.cuda.synchronize().[1] https://www.thonking.ai/p/what-shapes-do-matrix-multiplicati...
Para desplegar un modelo en una NPU se necesita bastante optimización basada en perfiles. Si tomas un modelo que corre bien en CPU y lo llevas sin optimizarlo para la NPU, por lo general los resultados son decepcionantes
La descripción del repositorio de GitHub es mucho más informativa que el blog
Si corres multiplicación de matrices int8 con onnx, el rendimiento es de alrededor de 0.6TF
https://github.com/usefulsensors/qc_npu_benchmark
Dicen que hicieron que la matriz de entrada fuera más cercana a una cuadrada para permitir tiling y reutilización, pero no me sorprendería que muchas de las optimizaciones posibles no hayan llegado a Onnx
Parece que Qualcomm no permite acceder directamente a la NPU, sino que espera que los usuarios conviertan el modelo con un framework y se lo pasen. En mi experiencia, las herramientas de conversión suelen ser bastante malas y se pierden muchas optimizaciones
Así que puede que no sea tanto que “la NPU sea mala”, sino que “la herramienta de conversión es mala”. Esperaré hasta poder acceder directamente, y no confío en las herramientas de conversión
Creo que las NPU son buenas para modelos de machine learning muy pequeños y aproximaciones de funciones muy rápidas. Ese es el uso que tengo en mente. Los LLM están de moda ahora, pero hay una enorme cantidad de tareas especializadas donde los modelos pequeños son realmente útiles
No quiero decir que no exista algo así, pero sinceramente no sé qué sería y me gustaría saberlo
Espero que la NPU del Elite X sea más fácil de acceder por Copilot+, pero el punto es que no puede ser tan fácil como “ejecuto un modelo genérico y mágicamente se teletransportará a la NPU”
Una RTX 4080 debería poder alcanzar alrededor de 40 TFLOPS, pero aquí solo reportan 216,000 millones de operaciones por segundo. Con eso me parece que habría que revisar el benchmark
Es muy probable que haya habido un error grave en la medición de FLOPS. Es posible que la CPU le gane a la NPU, pero para hacer una comparación correcta habría que benchmarkear varias multiplicaciones de matrices sin sincronización de la aplicación
El benchmark es una multiplicación de matrices con forma
(6, 1500, 256) X (6, 256, 1500), que en el mundo de la IA no es tan grande. Con matrices mucho más grandes, la brecha sería mayorPor ejemplo, incluso Llama 3.1 8B, que es uno de los modelos pequeños, tiene multiplicaciones de matrices como
(batch, 14336, 4096) x (batch, 4096, 14336)No creo que este benchmark sea suficientemente realista
Probé correr qprof, el profiler de NPU de Qualcomm, sobre este benchmark. Según el perfil, la carga se asignó a los núcleos vectoriales, no a los tensor cores que aportan la mayor parte de la potencia de cómputo de la NPU
Haciendo un cálculo aproximado, HMX parece ser 30 veces más potente que HVX
La carga de trabajo es relativamente pequeña, así que por el overhead de cuantización/descuantización de entrada/salida y el mapeo NCHW-NHCW no se aprovecha lo suficiente la capacidad del hardware. Rellenar los pesos y las entradas a múltiplos de 64 también ayudaría al rendimiento
Gráfico de profiling: https://imgur.com/a/2OKR93e
El rendimiento de cómputo estimado de HVX es
4 * 2 * 1.43 * 1024 / 8 = 1.46TOPSen int8. Aquí, 4 es la cantidad de núcleos vectoriales, 2 es la cantidad de operaciones por ciclo, 1.43GHz es la frecuencia de HVX, 1024bit es el ancho del registro vectorial y 8bit es la precisión4 * 2 * 1.43 * 1024 / 8El título real del artículo debería ser “Benchmarking Qualcomm's NPU on the Microsoft Surface Tablet”
Esto no es un artículo sobre las NPU en general, sino sobre una NPU específica vista con un benchmark específico y una combinación específica de bibliotecas/frameworks. Así que, en realidad, no demuestra casi nada