Lee esto primero
Las tablas de abajo no se leen sin esto. Viene de la documentación del propio benchmark, no de nosotros.
Todo aquí se midió en un Redmi Note 10 Pro (sweet, Adreno 618, 1080×2400, 120 Hz, Android 12), builds de release, 3 iteraciones por fase y un arranque de la app por iteración. Es una comparación entre cinco frameworks en un dispositivo, no una afirmación sobre ningún otro.
La pantalla va a 120 Hz y varias escenas tocan ese techo en más de un framework. Una fila en el techo dice «este framework no era el límite aquí» y nada más; separarlas exige una carga mayor, que es para lo que existe CFBENCH_LOAD.
Flashlight cuenta la ventana del app siendo presentada, lo que en cuatro de las cinco plataformas está desacoplado del costo de la escena: leyó exactamente 60,00 para una fase de React Native que no dibuja nada, y nunca superó 60,00 en 3388 muestras en una pantalla de 120 Hz. Así que los fotogramas son lo que cada app midió de sí misma; lo que aporta Flashlight es CPU y memoria, que ninguna app mide sobre sí.
Tasa de fotogramas, fase por fase
La medición que cada app hace de sí misma: en cuatro de las cinco plataformas un profiler externo mide la ventana, no la escena. Más es mejor.
| Fase | CrossFly | Flutter | React Native | Kotlin Multiplatform | .NET MAUI |
|---|---|---|---|---|---|
draw_rect 16 000 filled rects in a full-screen grid | 104.9 | 9.3 | 10.3 | 57.7 | 34.7 |
draw_text 16 000 short text runs | 55.2 | 6.7 | 13.9 | 9.9 | 8.0 |
draw_image 16 000 image quads from one texture | 87.9 | 7.6 | 12.7 | 35.2 | 27.8 |
cards as many realistic cards as fit at a fixed font scale | 118.8en el techo | 49.0 | 75.4 | 103.1 | 74.0 |
textfield 2400 real, interactive TextFields | 56.4 | 12.8 | 0.06 | 17.9 | 18.7 |
image_one_texture a 48×48 quad grid from one atlas page | 118.5en el techo | 53.9 | 72.6 | 119.7en el techo | 120.0en el techo |
shapes_lines a batched line every 1px across the surface | 111.4 | 29.3 | 120.1en el techo | 119.8en el techo | 119.9en el techo |
shapes_circles 50 concentric filled circles | 99.1 | 40.9 | 107.3 | 112.9 | 105.1 |
shapes_polygon_concave 3 nested 36-point concave stars | 118.6en el techo | 119.2en el techo | 89.0 | 117.2 | 87.4 |
shapes_polygon_convex 3 nested 36-side convex polygons | 119.6en el techo | 119.3en el techo | 120.0en el techo | 120.0en el techo | 120.0en el techo |
shapes_shadow 8 nested rounded rects with a 40px blurred shadow | 119.9en el techo | 110.2 | 119.4en el techo | 120.0en el techo | 120.0en el techo |
shapes_text 16 offset passes over a screenful of text | 80.3 | 46.3 | 117.6 | 73.6 | 44.1 |
sqlite_validate that the encryption below is real | 11/11 pass | 7/7 pass | 3/5 pass | no implementado | 7/7 pass |
sqlite_bench 100k rows × {plain, ChaCha20, SQLCipher} | 3 ciphers | 2 ciphers | 2 ciphers | no implementado | 2 ciphers |
Las celdas sombreadas están en el techo de 120 Hz de la pantalla. Significan «este framework no era el límite aquí» y nada más: son suelos, no resultados.
shapes_circles — 50 círculos concéntricos rellenos, unos 35 megapíxeles de overdraw opaco — corre más rápido en tres de los cuatro ports. Es el mismo trabajo en ambos lados: todos son Skia sobre el driver OpenGL ES de esta GPU, su tiempo de fotograma es lineal en el área pintada igual que el de la referencia, y lo que separa es el precio del píxel. Allí la referencia está limitada por CPU en su propio teselador.
Es en la memoria donde los frameworks se separan
Pico de PSS en las catorce fases. El suelo es lo que cobra un framework antes de dibujar nada; el techo es lo que cuesta su peor escena.
El suelo es el runtime más el contexto GL: el precio de entrada, antes de un solo píxel.
2400 campos de texto nativos — EditText, TextInput, Entry, BasicTextField — cuestan entre 321 y 601 MB. CrossFly dibuja sus 2400 y paga 132 MB, 14 MB por encima de su propio suelo. El costo no es dibujar campos de texto; es tenerlos como objetos de la plataforma.
Lo que se entrega
Release, firmado, solo arm64-v8a: cualquier otra cosa compara convenciones de empaquetado, no frameworks.
Las builds encogidas no son las que se midieron, salvo la de MAUI, que no puede construirse de otra forma. Y la tabla encogida no puede ordenarse tal cual: Kotlin Multiplatform no implementa las fases de SQLite, así que no lleva motor, mientras que el de la referencia son 1,82 MB de sus 2,66 MB. Restándolo, los dos quedan a 0,04 MB.
SQLite: la diferencia es la capa de binding
100 000 filas, el mismo SQL, el mismo journal mode y synchronous en todas las plataformas. Los tiempos de React Native quedan fuera porque su propia fase de validación falla dos de cinco comprobaciones, que es justo para lo que existe.
| Medición | CrossFly | Flutter | React Native | .NET MAUI |
|---|---|---|---|---|
| plain insert, 100 000 rows | 216 ms | 251 ms | — | 1248 ms |
| rows/s | 463 000 | 398 000 | — | 80 100 |
| plain bulk insert | 154 ms | 232 ms | — | 1242 ms |
| ciphers run | plain, chacha20, sqlcipher | plain, sqlcipher | plain, sqlcipher | plain, sqlcipher |
| validation | 11/11 | 7/7 | 3/5 | 7/7 |
Menos capas entre tú y la GPU
Cada salto que da un fotograma cuesta tiempo. CrossFly dibuja directamente a través de LibGDX — sin puente de JavaScript que cruzar.
Todo lo que matiza los números de arriba
Entero, no resumido. Un benchmark sin sus advertencias es un anuncio.
Todo aquí se midió en un Redmi Note 10 Pro (sweet, Adreno 618, 1080×2400, 120 Hz, Android 12), builds de release, 3 iteraciones por fase y un arranque de la app por iteración. Es una comparación entre cinco frameworks en un dispositivo, no una afirmación sobre ningún otro.
La pantalla va a 120 Hz y varias escenas tocan ese techo en más de un framework. Una fila en el techo dice «este framework no era el límite aquí» y nada más; separarlas exige una carga mayor, que es para lo que existe CFBENCH_LOAD.
Flashlight cuenta la ventana del app siendo presentada, lo que en cuatro de las cinco plataformas está desacoplado del costo de la escena: leyó exactamente 60,00 para una fase de React Native que no dibuja nada, y nunca superó 60,00 en 3388 muestras en una pantalla de 120 Hz. Así que los fotogramas son lo que cada app midió de sí misma; lo que aporta Flashlight es CPU y memoria, que ninguna app mide sobre sí.
Una escena a 120 fps envía ocho veces más fotogramas que una a 14, así que su CPU es mayor por una razón que nada tiene que ver con la eficiencia. Divide por el número de fotogramas antes de comparar dos celdas.
Las filas draw_*, textfield y shapes_text varían esa cantidad entre ejecuciones sobre un build sin cambios, así que una diferencia menor es ruido.
Kotlin Multiplatform no tiene las fases de SQLite y reporta el hueco en vez de saltárselo. La validación de SQLite de React Native falla dos de cinco comprobaciones — que es la fase diciendo que los tiempos de al lado no son fiables — así que quedan fuera en vez de publicarse.
La app de benchmark, los cuatro ports y los scripts de medición son un solo repositorio. Unos doce minutos por plataforma.
flashlight/run-all.sh flashlight/results/crossfly dev.crossfly.benchmark python flashlight/summarize.py flashlight/results/crossfly