Documentación
Informe de rendimiento
Lo que cuestan las apps de NSH, medido. Cómo NSH las mantiene rápidas y cómo medir tu propia app se explica en Rendimiento.
#Valores de referencia
De las mediciones base del propio NSH (Apple silicon salvo indicación contraria):
| Escenario | Cifras |
|---|---|
| App contador, headless (debug) | Primer frame ~0.5–0.65 ms (58 ops / 49 mount items); un toque ~0.15 ms |
| App contador, Mac Catalyst (release) | Primer frame ~1.05–1.15 ms (unos 0.7 ms se dedican a maquetar 10 textos con CoreText por primera vez); un toque ~0.12 ms |
| Lista de fotos de Gallery, headless (debug) | Primer frame 653 ops / 224 nodos, ~2.7 ms |
| Scroll de la lista de fotos, Catalyst (release) | Revincular una fila mientras se hace scroll lento ~0.4 ms; una pantalla completa de ~32 filas en un fling rápido 0.9–1.6 ms |
| Scroll de la lista de fotos, emulador de Android (arm64, release) | Revincular una fila ~0.3–0.4 ms (JS ~60 µs) |
| Arranque del motor (release) | ~75–155 µs; las APIs globales (fetch, URL, …) no cuestan nada hasta el primer uso, y luego ~90 µs |
| Hot reload | ~120 ms desde que se guarda un archivo hasta que la nueva versión está en ejecución; aplicar la actualización 0.2–0.5 ms |
#Linux (GTK 4)
Medido en un escritorio Linux (x86_64, 24 hilos, GPU NVIDIA), con builds release, la galería en una ventana de 1896 × 1030 en una
pantalla de 120 Hz, con cada lectura tomada automáticamente por examples/gallery/tools/perf.py (nsh test --target linux --fps,
una línea por segundo; la CPU corresponde al proceso completo, en porcentaje de un núcleo). Frame p95 es el tiempo del hilo principal por frame dibujado
en el percentil 95: por debajo de 8.3 ms, un frame cabe en un refresco de 120 Hz.
#Demos animadas
| Demo | FPS | Frame p95 | CPU |
|---|---|---|---|
| Stocks: 22 sparklines en vivo, un gráfico intradía, libro de órdenes, cinta | 117–120 | 5–6 ms | 25–29% (1200 × 800), 28–32% (1896 × 1030) |
| Stocks, con la ventana llenando una pantalla 4K de 144 Hz a 1.6× | 143–145 | 4.1–4.4 ms | 26–29% |
| Canvas: un indicador, un reloj, una matriz de puntos | 120 | 2.6–4.4 ms | 5–10% |
| Animations: tres bucles nativos, springs, secuencias | 120 | 2.0 ms | 12–13% |
#Todas las páginas
Todas las páginas de la galería, al abrirse y mientras la prueba hace scroll en ellas, en una ventana de 1200 × 800 en una pantalla de 144 Hz (la
misma máquina). NSH dibuja un frame solo cuando algo cambia en pantalla, así que FPS aquí es la tasa mientras algo
se mueve: un frame por cada intervalo mediano entre frames dibujados (moving de perf.py); a los 144 de la pantalla, cada frame de una
animación o de un scroll llega un refresco después del anterior. Sobre un refresco es la proporción de frames que tardaron más
que un refresco (6.9 ms). Los GIF y WebP de Images se reproducen a sus propias tasas de frames (unos 50 FPS aquí), igual que el video. Physics, Overlays y Menus son cortas: en 1200 × 800 un scroll llega a su final en pocos frames, por lo que se miden en una ventana de 900 × 500, donde tienen espacio para hacer scroll.
| Página | Origen de los frames | FPS | Frame p95 | Sobre un refresco | CPU |
|---|---|---|---|---|---|
| Stocks | Datos en vivo, bucles nativos | 144 | 4.1 ms | 0% | 26% |
| Stocks, con scroll | Datos en vivo, scroll | 144 | 4.1 ms | 0% | 25% |
| Photos, con scroll | Scroll | 144 | 2.8 ms | 2% | 9% |
| Lists, con scroll | Scroll | 143 | 1.7 ms | 0% | 7% |
| Scrolling, con scroll | Scroll | 144 | 1.6 ms | 1% | 4% |
| Navigation, con scroll | Scroll | 143 | 1.7 ms | 0% | 2% |
| Form, con scroll | Scroll | 144 | 1.3 ms | 0% | 4% |
| Resizable, con scroll | Scroll | 143 | 0.6 ms | 0% | 1% |
| Dock, con scroll | Scroll | 142 | 0.5 ms | 1% | 1% |
| Gestures | Springs, un anillo pulsante | 144 | 1.6 ms | 0% | 1–5% |
| Physics, con scroll | Scroll (ventana de 900 × 500) | 144 | 1.6 ms | 1% | 1% |
| Animations | Bucles nativos | 144 | 2.6 ms | 0–1% | 5% |
| Canvas, con scroll | Scroll, bucles nativos | 144 | 4.2 ms | 0% | 1–4% |
| Styles, con scroll | Scroll | 144 | 2.3 ms | 2% | 3% |
| Tailwind | Utilidades animate-* |
144 | 1.7 ms | 0% | 3–8% |
| Text, con scroll | Scroll | 144 | 1.9 ms | 0% | 4% |
| Text View, con scroll | Scroll | 141 | 1.8 ms | 2% | 7% |
| Images, con scroll | Scroll, frames de GIF y WebP | 143 | 2.9 ms | 2% | 8% |
| Components, con scroll | Scroll | 144 | 1.4 ms | 0% | 3% |
| Overlays, con scroll | Scroll (ventana de 900 × 500) | 144 | 1.7 ms | 0% | 2% |
| Menus, con scroll | Scroll (ventana de 900 × 500) | 144 | 1.9 ms | 0% | 2% |
| Controls | Transiciones | 144 | 0.9 ms | 0% | 1–4% |
| Web and video | Frames de video, la página | 135–142 | 1.0 ms | 0% | 7–8% |
#Memoria
En reposo, 3 s después del arranque (runners release, ventana de 480 × 640). Private es la memoria que posee únicamente el proceso (USS); resident suma las páginas de las bibliotecas compartidas (GTK, el controlador de la GPU), que comparten todas las apps GTK de la máquina:
| App | Renderer | Private (USS) | Resident (RSS) |
|---|---|---|---|
App en Rust (nsh init --template rust) |
GL (ngl) |
~49 MB | ~142 MB |
| App en Rust | Cairo (GSK_RENDERER=cairo) |
~28 MB | ~95 MB |
Contador en TSX (examples/counter) |
GL | ~79 MB | ~166 MB |
| Ventana GTK 4 con una etiqueta, como referencia | Vulkan / Cairo | ~62 / ~40 MB | ~132 / ~109 MB |
Volver a visitar una página no añade memoria; la primera visita a una página hace crecer el proceso por lo que almacena en caché a propósito (imágenes decodificadas, con un límite de 128 MB) y por las bibliotecas que carga en el primer uso (WebKitGTK para las vistas web).
#Tamaño de la app
Una app hello world creada por nsh init con cada plantilla, y dos apps que la gente usa, tal como nsh package --target linux
las compila para x86_64. El runner es el único ejecutable de la app; el archive es el .tar.gz con el que se distribuye. Los hello
worlds se compilan con link-time optimization y se les quitan los símbolos (CARGO_PROFILE_RELEASE_LTO=fat,
CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1, CARGO_PROFILE_RELEASE_STRIP=true):
| App | Escrita en | Runner | Archive |
|---|---|---|---|
Hello world (--template rust) |
Rust | 16.5 MB | 7.5 MB |
Hello world (--template ts) |
TSX, sobre QuickJS | 25.7 MB | 11.6 MB |
Hello world (--template mixed) |
TSX y un crate de Rust | 25.8 MB | 11.7 MB |
| sysmon 0.1.3 | Rust | 20.9 MB sin símbolos | 9.7 MB |
| Omasend 0.2.0 | Rust | 22.3 MB | 9.8 MB |
Los runners de TSX contienen el runtime de QuickJS, los módulos nativos y el renderer; el código propio de una app es su bundle, junto al runner, de menos de 1 KB para un hello world.
Qué hace posibles estas cifras:
- El renderer: con el controlador de NVIDIA, NSH dibuja con el renderer GL de GSK, cuyos frames cuestan una fracción de los del renderer
Vulkan en ese caso (la demo Animations: ~1 ms de dibujo por frame en lugar de ~9.5 ms).
GSK_RENDERERsigue teniendo la última palabra. - Rasterizados del canvas: cada canvas se rasteriza una vez en tiles de 256 px que se mantienen en la GPU. Un valor animado vuelve a dibujar solo el área que cubren las operaciones que lo leen, sobre los píxeles conservados de la parte estática del dibujo; un canvas fuera de la vista espera hasta que entra en pantalla con el scroll.
- Paths reservados una sola vez:
fillRectyfillTextdespués de un path largo lo dejan donde está hasta que se vuelve a usar (una línea de 400 puntos y luego 240 barras: 0.63 ms).
#macOS (AppKit)
Medido en un MacBook Pro (Apple M3, 24 GB) con su pantalla ProMotion integrada de 120 Hz, con builds release, la galería en
una ventana de 1200 × 800, con cada lectura tomada automáticamente por examples/gallery/tools/perf.py (nsh test --target macos --fps, una línea por segundo; la CPU corresponde al proceso completo, en porcentaje de un núcleo, y la GPU a su parte de la GPU). Las fases son
las del hilo principal por frame: tick (las animaciones nativas del display link) y canvas (la reproducción de los dibujos).
#Demos animadas
| Demo | FPS | Frame p95 | CPU | GPU |
|---|---|---|---|---|
| Stocks: 22 sparklines en vivo, un gráfico intradía, libro de órdenes, cinta | 120 | 2.7–3.7 ms | 33–36% | 18–22% |
| Canvas: un indicador, un reloj, una matriz de puntos | 119–120 | 0.6–0.7 ms | 4–7% | 0% |
| Animations: tres bucles nativos, springs, secuencias | 119–120 | 0.3–1.6 ms | 5–8% | 0% |
En Stocks, el tiempo del hilo principal por frame es de ~1.6 ms de ticks, de los cuales ~0.45 ms reproducen las partes animadas de los canvas; Core Animation dibuja el resto de cada canvas en sus propios hilos.
#Todas las páginas
Todas las páginas de la galería, al abrirse y mientras la prueba hace scroll en ellas, en la misma ventana de 1200 × 800. Como en Linux, FPS es la tasa mientras algo se mueve (un frame por cada intervalo mediano entre frames dibujados); a los 120 de la pantalla, cada frame de una animación o de un scroll llega un refresco después del anterior. Sobre un refresco es la proporción de frames que tardaron más que un refresco (8.3 ms). Una página en reposo no dibuja ningún frame y usa un 2–5% de CPU. Los GIF y WebP de Images se reproducen en Core Animation y el video en AVFoundation, a sus propias tasas de frames.
| Página | Origen de los frames | FPS | Frame p95 | Sobre un refresco | CPU |
|---|---|---|---|---|---|
| Stocks | Datos en vivo, bucles nativos | 120 | 3.2 ms | 0% | 35% |
| Stocks, con scroll | Datos en vivo, scroll | 120 | 3.5 ms | 0% | 36% |
| AI Chat, con scroll | Scroll | 120 | 2.4 ms | 1% | 5% |
| Photos, con scroll | Scroll | 120 | 1.4 ms | 0% | 10% |
| Lists, con scroll | Scroll | 120 | 1.6 ms | 0% | 8% |
| Scrolling, con scroll | Scroll | 120 | 2.7 ms | 0% | 6% |
| Navigation, con scroll | Scroll | 120 | 0.2 ms | 0% | 4% |
| Form, con scroll | Scroll | 120 | 0.6 ms | 0% | 5% |
| Resizable, con scroll | Scroll | 120 | 0.3 ms | 0% | 4% |
| Dock, con scroll | Scroll | 120 | 0.3 ms | 1% | 5% |
| Gestures | Springs, un anillo pulsante | 120 | 0.4 ms | 0% | 4% |
| Physics, con scroll | Scroll | 120 | 0.3 ms | 0% | 4% |
| Animations | Bucles nativos | 120 | 0.3 ms | 0% | 5% |
| Canvas, con scroll | Scroll, bucles nativos | 120 | 1.2 ms | 0% | 7% |
| Styles, con scroll | Scroll, una transformación oscilante | 120 | 1.0 ms | 0% | 6% |
| Tailwind | Utilidades animate-* |
120 | 0.5 ms | 0% | 4% |
| Text, con scroll | Scroll | 120 | 0.3 ms | 0% | 5% |
| Text View, con scroll | Scroll | 120 | 0.7 ms | 0% | 5% |
| Images, con scroll | Scroll | 120 | 0.4 ms | 0% | 5% |
| Components, con scroll | Scroll | 120 | 0.3 ms | 0% | 5% |
| Overlays, con scroll | Scroll | 120 | 0.3 ms | 0% | 4% |
| Menus, con scroll | Scroll | 120 | 0.3 ms | 0% | 5% |
| Controls | Transiciones | 120 | 0.3 ms | 0% | 4% |
| Web and video, con scroll | Scroll | 120 | 0.2 ms | 0% | 6% |
| System | requestAnimationFrame, temporizadores |
120 | 0.2 ms | 0% | 3% |
| Standard APIs, con scroll | Scroll | 120 | 0.3 ms | 0% | 5% |
| Storage, con scroll | Scroll | 120 | 0.3 ms | 0% | 5% |
#Memoria
En reposo, 3 s después del arranque (runners release). Footprint es la memoria que macOS carga al proceso (lo que muestra el Monitor de Actividad); resident suma las páginas de las bibliotecas compartidas del sistema:
| App | Ventana | Footprint | Resident (RSS) |
|---|---|---|---|
App en Rust (nsh init --template rust) |
1200 × 800 | ~19 MB | ~86 MB |
Contador en TSX (examples/counter) |
1200 × 800 | ~20 MB | ~89 MB |
| Ventana AppKit con una etiqueta, como referencia | 480 × 640 | ~17 MB | ~80 MB |
A lo largo del recorrido por la galería, el proceso ocupa 84–230 MB hasta la página Canvas y ~350–360 MB a partir de la página Text View, con lo que almacena en caché a propósito (imágenes decodificadas, con un límite de 128 MB; los documentos de la página Text View) y los frameworks que carga en el primer uso (WebKit para las vistas web); los rasterizados de la página Canvas, de hasta ~310 MB con los tres canvas en pantalla, se liberan cuando la página se cierra.
#Tamaño de la app
Las apps hello world que crea nsh init con cada plantilla, tal como las compila nsh package --target macos para Apple
silicon (arm64), con optimización en tiempo de enlazado y sin símbolos, como en Linux, y las dos apps anteriores tal como se distribuyen para Apple
silicon. El ejecutable es el runner dentro de .app; el zip es el .app comprimido (ditto -c -k):
| App | Escrita en | Ejecutable | Zip .app |
|---|---|---|---|
Hello world (--template rust) |
Rust | 12.4 MB | 6.0 MB |
Hello world (--template ts) |
TSX, sobre QuickJS | 19.8 MB | 9.5 MB |
Hello world (--template mixed) |
TSX y un crate de Rust | 19.8 MB | 9.5 MB |
| sysmon 0.1.3 | Rust | 21.6 MB | 8.4 MB (.tar.gz de su release) |
| Omasend 0.2.0 | Rust | 18.4 MB | 9.7 MB (.tar.gz de su release) |
Qué hace posibles estas cifras:
- Fotogramas solo para los cambios: el display link se activa mientras se ejecuta una animación nativa y se detiene con ella; una animación
se detiene con su página (la eliminación de su vista termina sus fotogramas clave, un bucle se detiene en el
cleanupde su componente). - Canvas dibujados donde cuestan menos: un canvas que se pinta con frecuencia (una animación, datos en vivo) se dibuja en los hilos de Core
Animation (
drawsAsynchronously), lo que mantiene libre el hilo principal; uno que se redibuja de vez en cuando se dibuja de inmediato, sin la memoria gráfica que retiene el dibujo asíncrono. Un valor animado redibuja solo el área que cubren las operaciones que lo leen. - Partes estáticas del canvas conservadas: cuando un dibujo permanece sin cambios, una parte larga anterior a su primera operación animada se conserva como una capa; cada fotograma dibuja esa capa y reproduce solo el resto animado, de inmediato en el hilo principal (el indicador, el reloj y la matriz de puntos de la página Canvas: sin trabajo de GPU y con la mitad de memoria).
- Contenido dibujado cuando cambia: los textos y los canvas se dibujan en capas propias, de nuevo cuando cambia su contenido o su tamaño, no cuando se mueven o se desplazan.
#Windows (Win32, Direct2D)
Medido en un equipo de escritorio con Windows 11 (Intel Core i7-13700KF, 32 GB, NVIDIA GeForce RTX 3060 Ti) con una pantalla 4K de 144 Hz al
150 %, compilaciones release, la galería en una ventana de 1200 × 800 (1800 × 1200 píxeles), con cada lectura tomada automáticamente por
examples/gallery/tools/perf.py (nsh test --target windows --fps, una línea por segundo; la CPU es la del proceso completo, en
porcentaje de un núcleo). Las fases son las del hilo principal por fotograma: paint (Direct2D dibujando la ventana) y, dentro
de ella, canvas (grabación y reproducción de dibujos).
#Demos animadas
| Demo | FPS | Fotograma p95 | Paint / canvas por fotograma | CPU |
|---|---|---|---|---|
| Stocks: 22 sparklines en vivo, un gráfico intradía, libro de órdenes, cinta | 144 | 3.7–4.3 ms | 1.4 / 0.13 ms | 36–43% |
| Canvas: un indicador, un reloj, una matriz de puntos | 144 | 1.0–2.0 ms | 0.8–1.4 / 0.05 ms | 21–35% |
| Animations: tres bucles nativos, springs, secuencias | 144 | 1.0–3.6 ms | 0.9–1.9 ms | 20–41% |
Stocks confirma cambios entre 16 y 17 veces por segundo, cada uno en hasta 2.1 ms de JS y 1.7 ms del núcleo en el hilo del motor (p95).
#Todas las páginas
Todas las páginas de la galería, al abrirse y mientras la prueba se desplaza por ellas, en la misma ventana de 1200 × 800. Como en Linux y macOS, FPS es la tasa mientras algo se mueve (un fotograma por cada intervalo mediano entre fotogramas dibujados); a los 144 de la pantalla, cada fotograma de una animación o de un desplazamiento llega un refresco después del anterior. Por encima de un refresco es la proporción de fotogramas que tardaron más que un refresco (6.9 ms). Una página en reposo no dibuja ningún fotograma y usa entre 0 y 2% de CPU. Los GIF y WebP de Images se reproducen a sus propias tasas de fotogramas (unos 40 FPS aquí). Physics, Overlays, Menus y Storage se miden en una ventana de 900 × 500, donde tienen espacio para desplazarse.
| Página | Origen de los fotogramas | FPS | Fotograma p95 | Por encima de un refresco | CPU |
|---|---|---|---|---|---|
| Stocks | Datos en vivo, bucles nativos | 144 | 3.9 ms | 0% | 36% |
| Stocks, scrolling | Datos en vivo, desplazamiento | 144 | 4.3 ms | 0% | 43% |
| Photos, scrolling | Desplazamiento | 143 | 2.1 ms | 0% | 9% |
| Lists, scrolling | Desplazamiento | 144 | 2.0 ms | 1% | 19% |
| Scrolling, scrolling | Desplazamiento | 143 | 2.1 ms | 0% | 7% |
| Navigation, scrolling | Desplazamiento | 144 | 0.8 ms | 0% | 9% |
| Form, scrolling | Desplazamiento | 144 | 0.8 ms | 0% | 12% |
| Resizable, scrolling | Desplazamiento | 143 | 0.4 ms | 0% | 4% |
| Dock, scrolling | Desplazamiento | 144 | 0.5 ms | 1% | 5% |
| Gestures | Springs, un anillo que pulsa | 144 | 1.8 ms | 0% | 36% |
| Physics, scrolling | Desplazamiento (una ventana de 900 × 500) | 144 | 0.8 ms | 0% | 4% |
| Animations | Bucles nativos | 144 | 1.0 ms | 0% | 28% |
| Canvas, scrolling | Desplazamiento, bucles nativos | 144 | 2.0 ms | 0% | 35% |
| Styles, scrolling | Desplazamiento, una transformación oscilante | 144 | 2.1 ms | 0% | 39% |
| Tailwind | Utilidades animate-* |
144 | 0.6 ms | 0% | 19% |
| Text, scrolling | Desplazamiento | 144 | 1.6 ms | 0% | 8% |
| Text View, scrolling | Desplazamiento | 144 | 1.1 ms | 0% | 10% |
| Images, scrolling | Desplazamiento, fotogramas de GIF y WebP | 143 | 4.8 ms | 2% | 23% |
| Components, scrolling | Desplazamiento | 144 | 1.0 ms | 1% | 11% |
| Overlays, scrolling | Desplazamiento (una ventana de 900 × 500) | 144 | 0.6 ms | 0% | 1% |
| Menus, scrolling | Desplazamiento (una ventana de 900 × 500) | 144 | 0.6 ms | 0% | 2% |
| Controls | Transiciones | 117–144 | 1.5 ms | 0% | 22% |
| Web and video, scrolling | Desplazamiento | 144 | 1.6 ms | 0% | 15% |
| System | requestAnimationFrame, temporizadores |
144 | < 0.1 ms | 0% | 1% |
| Storage, scrolling | Desplazamiento (una ventana de 900 × 500) | 144 | 0.5 ms | 0% | 1% |
#Memoria
En reposo, 3 s después del inicio (runners release, una ventana de 1200 × 800). El conjunto de trabajo privado es la memoria que usa el proceso por sí solo (la columna Memoria del Administrador de tareas); los bytes privados son lo que tiene confirmado; el conjunto de trabajo añade las páginas de las bibliotecas del sistema que comparte con otros procesos (Direct3D, Direct2D, DirectWrite, el controlador de la GPU):
| App | Conjunto de trabajo privado | Bytes privados | Conjunto de trabajo |
|---|---|---|---|
App de Rust (nsh init --template rust) |
~14 MB | ~60 MB | ~53–71 MB |
Contador en TSX (examples/counter) |
~15 MB | ~61 MB | ~56–71 MB |
A lo largo del recorrido por la galería, el conjunto de trabajo es de ~108–115 MB en Stocks, ~230 MB desde la página Canvas, ~320–340 MB desde la página Text View y ~390–405 MB una vez que Web and video ha abierto WebView2 (cuyas páginas se ejecutan en los procesos propios de WebView2), incluyendo lo que se guarda en caché a propósito (imágenes decodificadas, limitadas a 128 MB; los documentos de la página Text View).
#Tamaño de la app
Las apps hello world que crea nsh init con cada plantilla, tal como las compila nsh package --target windows para x86_64
(los runners de Rust con optimización en tiempo de enlazado y sin símbolos, como en Linux). El ejecutable es el <Name>.exe de la app;
el zip es el .zip escrito junto a su directorio:
| App | Escrita en | Ejecutable | Zip |
|---|---|---|---|
Hello world (--template rust) |
Rust | 15.0 MB | 6.7 MB |
Hello world (--template ts) |
TSX, sobre QuickJS | 28.6 MB | 12.4 MB |
Hello world (--template mixed) |
TSX y un crate de Rust | 26.3 MB | 11.5 MB |
Qué hace posibles estas cifras:
- Fotogramas solo para los cambios, dibujados solo donde cambiaron: el ticker sincronizado con el compositor se ejecuta mientras corre una animación nativa, una transición o un callback de fotograma. Un fotograma dibuja las áreas de las vistas que cambiaron (donde estaban y donde están), sobre los píxeles que conserva la ventana, y presenta solo esas al compositor (Stocks: ~34 vistas por fotograma).
- Los canvas graban su parte animada: un dibujo se graba como listas de comandos de Direct2D, vectoriales y nítidas a cualquier escala. Cuando cambia un valor animado, solo se vuelven a grabar las operaciones desde la primera que lee un valor animado en adelante; lo que viene antes se conserva (Stocks: ~26 ms de grabación por segundo para 22 sparklines que pulsan y el gráfico).
- Solo se dibuja lo que se ve: los hijos de una vista de desplazamiento o de una vista con recorte que quedan fuera de la vista se omiten en el fotograma, y las vistas sin contenido propio no dibujan más que su fondo y su borde.
#iOS (UIKit)
Medido en el simulador de iOS (un iPhone 18 Pro Max, en el MacBook Pro anterior), compilaciones release, con el mismo recorrido
(perf.py --target ios: nsh test --target ios --fps, los toques de un dedo tal como los recibe UIKit). El simulador presenta
a 60 Hz; en un iPhone con pantalla ProMotion, los display links de NSH se ejecutan a los 120 Hz de la pantalla (las apps que compila nsh package
y el host de desarrollo lo solicitan con CADisableMinimumFrameDurationOnPhone). Las fases son tick (animaciones
nativas), drain (aplicación de los lotes del motor) y scroll (lo que sigue a una vista de desplazamiento: listas, celdas fijas,
valores enlazados).
| Página | Origen de los fotogramas | FPS | Fotograma p95 | CPU |
|---|---|---|---|---|
| Home, scrolling | Desplazamiento | 60 | 0.1 ms | 6% |
| Stocks | Datos en vivo, bucles nativos | 60 | 0.3 ms | 22% |
| Stocks, scrolling | Datos en vivo, desplazamiento | 60 | 0.3 ms | 25% |
| Photos, scrolling | Desplazamiento | 60 | 1.2 ms | 6% |
| Lists, scrolling | Desplazamiento | 60 | 1.1 ms | 8% |
| Scrolling, scrolling | Desplazamiento | 60 | 0.1 ms | 7% |
| Navigation, scrolling | Desplazamiento | 60 | 0.2 ms | 7% |
| Form, scrolling | Desplazamiento | 60 | 0.3 ms | 7% |
| Gestures | Springs, un anillo que pulsa | 60 | 0.3 ms | 3% |
| Animations | Bucles nativos | 60 | 0.7 ms | 6% |
| Canvas | Bucles nativos | 60 | 0.4 ms | 16% |
| Styles | Una transformación oscilante | 60 | 0.4 ms | 4% |
| Tailwind, scrolling | Desplazamiento | 60 | 0.2 ms | 7% |
| Text, scrolling | Desplazamiento | 60 | 0.1 ms | 7% |
| Text View, scrolling | Desplazamiento | 60 | 0.1 ms | 6% |
| Images, scrolling | Desplazamiento | 60 | 0.1 ms | 5% |
| Components, scrolling | Desplazamiento | 60 | 0.2 ms | 6% |
| Overlays, scrolling | Desplazamiento | 60 | 0.1 ms | 6% |
| Menus, scrolling | Desplazamiento | 60 | 0.2 ms | 7% |
| Controls, scrolling | Desplazamiento, transiciones | 60 | 0.2 ms | 5% |
| Web and video, scrolling | Desplazamiento | 60 | 0.1 ms | 8% |
| System | requestAnimationFrame, temporizadores |
60 | 0.3 ms | 4% |
| Standard APIs, scrolling | Desplazamiento | 60 | 0.1 ms | 7% |
| Storage, scrolling | Desplazamiento | 60 | 0.1 ms | 8% |
FPS es la tasa mientras algo se mueve, como arriba: a los 60 del simulador, cada fotograma llega un refresco después del anterior. Toda página en la que nada se mueve no dibuja ningún fotograma y usa entre 1 y 2% de CPU en reposo. El proceso ocupa 40 MB en la página de inicio de la galería y ~180 MB después de todo el recorrido (imágenes guardadas en caché a propósito, los documentos de la página Text View).
En un iPhone 17 Pro (ProMotion, 120 Hz), con el host de desarrollo y una demo en pantalla, las páginas animadas dibujan en cada refresco:
| Página | Origen de los fotogramas | FPS | Fotograma p95 | Por encima de un refresco | CPU | Memoria |
|---|---|---|---|---|---|---|
| Stocks | Datos en vivo, bucles nativos | 120 | 0.2–0.3 ms | 0% | 46–70% | 41–43 MB |
| Canvas | Bucles nativos | 120 | 0.1 ms | 0% | 25–36% | 37–38 MB |
| Animations | Bucles nativos | 120 | 0.2 ms | 0% | 3–11% | 37 MB |
Stocks confirma cambios 20 veces por segundo (un tick cada 50 ms), cada uno en 1.5 ms de JS y 1.4 ms del núcleo en el hilo del motor (p95 de 2.2 y 2.0 ms), que está ocupado entre el 4 y el 6% del tiempo.
#Medir una app
Para medir una app de la misma manera, ejecuta sus pruebas o pasos con --fps y -v en --target linux, macos, windows o ios:
cargo build --release -p nsh-cli
python3 examples/gallery/tools/perf.py --target macos --only Stocks,Canvas # the gallery, page by page
echo 'await app.wait(5000)' | nsh -v test App.tsx --target linux --fps # any app