Pular para o conteúdo
CrossFly é 100% open sourceSiga no GitHub
CrossFlyCrossFly
Começar
Benchmark

Cinco frameworks, o mesmo trabalho, um celular

Quatorze fases, reimplementadas cena a cena em Flutter, React Native, .NET MAUI e Kotlin Multiplatform, rodadas de ponta a ponta sem um toque. Todo número desta página saiu de um aparelho; nenhum foi escolhido.

Medido em Redmi Note 10 Prosweet, Adreno 618, 1080×2400, 120 Hz, Android 123 iterações por fase
CrossFly CoreCrossFly Sync

Leia isto antes

As tabelas abaixo não se leem sem isto. Vem da documentação do próprio benchmark, não de nós.

Um celular, uma versão da especificação

Tudo aqui foi medido num Redmi Note 10 Pro (sweet, Adreno 618, 1080×2400, 120 Hz, Android 12), builds de release, 3 iterações por fase e uma abertura do app por iteração. É uma comparação entre cinco frameworks num aparelho, não uma afirmação sobre qualquer outro.

Linhas em 120 fps são pisos, não resultados

A tela roda a 120 Hz, e várias cenas encostam nesse teto em mais de um framework. Uma linha no teto diz "este framework não era o limite aqui" e nada mais; separá-las exige uma carga maior, que é para isso que existe o CFBENCH_LOAD do benchmark.

A taxa de quadros é a do próprio app

O Flashlight conta a janela do app sendo apresentada, o que em quatro das cinco plataformas está desacoplado do custo da cena — ele leu exatamente 60,00 para uma fase do React Native que não desenha nada, e nunca passou de 60,00 em 3388 amostras numa tela de 120 Hz. Então a taxa de quadros é a que cada app mediu de si mesmo; o que o Flashlight acrescenta é CPU e memória, que nenhum app mede sobre si.

Taxa de quadros, fase a fase

A medição que cada app faz de si mesmo — em quatro das cinco plataformas um profiler externo mede a janela, não a cena. Mais é melhor.

FaseCrossFlyFlutterReact NativeKotlin Multiplatform.NET MAUI
draw_rect
16 000 filled rects in a full-screen grid
104.99.310.357.734.7
draw_text
16 000 short text runs
55.26.713.99.98.0
draw_image
16 000 image quads from one texture
87.97.612.735.227.8
cards
as many realistic cards as fit at a fixed font scale
118.8no teto49.075.4103.174.0
textfield
2400 real, interactive TextFields
56.412.80.0617.918.7
image_one_texture
a 48×48 quad grid from one atlas page
118.5no teto53.972.6119.7no teto120.0no teto
shapes_lines
a batched line every 1px across the surface
111.429.3120.1no teto119.8no teto119.9no teto
shapes_circles
50 concentric filled circles
99.140.9107.3112.9105.1
shapes_polygon_concave
3 nested 36-point concave stars
118.6no teto119.2no teto89.0117.287.4
shapes_polygon_convex
3 nested 36-side convex polygons
119.6no teto119.3no teto120.0no teto120.0no teto120.0no teto
shapes_shadow
8 nested rounded rects with a 40px blurred shadow
119.9no teto110.2119.4no teto120.0no teto120.0no teto
shapes_text
16 offset passes over a screenful of text
80.346.3117.673.644.1
sqlite_validate
that the encryption below is real
11/11 pass7/7 pass3/5 passnão implementado7/7 pass
sqlite_bench
100k rows × {plain, ChaCha20, SQLCipher}
3 ciphers2 ciphers2 ciphersnão implementado2 ciphers

As células sombreadas estão no teto de 120 Hz da tela. Significam "este framework não era o limite aqui" e nada mais — são pisos, não resultados.

A linha em que um port ganha

shapes_circles — 50 círculos concêntricos preenchidos, cerca de 35 megapixels de overdraw opaco — roda mais rápido em três dos quatro ports. É o mesmo trabalho dos dois lados: todos são Skia sobre o driver OpenGL ES desta GPU, o tempo de quadro deles é linear na área pintada exatamente como o da referência, e o que separa é o preço do pixel. Ali a referência está limitada pela CPU no próprio tesselador.

É na memória que os frameworks se separam

Pico de PSS nas quatorze fases. O piso é o que o framework cobra antes de desenhar qualquer coisa; o teto é o que a pior cena custa.

CrossFly118–135 MB · 2400 campos de texto 132 MB
pisoteto
Kotlin Multiplatform121–227 MB · 2400 campos de texto 341 MB
pisoteto
React Native126–591 MB · 2400 campos de texto 601 MB
pisoteto
Flutter152–286 MB · 2400 campos de texto 556 MB
pisoteto
.NET MAUI174–203 MB · 2400 campos de texto 321 MB
pisoteto

O piso é o runtime mais o contexto GL — o preço da entrada, antes de um único pixel.

A única cena em que todo port paga e a referência não

2400 campos de texto nativos — EditText, TextInput, Entry, BasicTextField — custam entre 321 e 601 MB. O CrossFly desenha os 2400 sozinho e paga 132 MB, 14 MB acima do próprio piso. O custo não é desenhar campos de texto; é tê-los como objetos da plataforma.

O que é entregue

Release, assinado, só arm64-v8a — qualquer outra coisa compara convenções de empacotamento, não frameworks.

Sem encolher — R8 desligado
CrossFly
5.95 MB
Flutter
22.65 MB
Kotlin Multiplatform
6.17 MB
React Native
29.69 MB
.NET MAUI
37.34 MB
Encolhido — só o código que cada app alcança
CrossFly
2.66 MB−55%
Flutter
20.74 MB−8%
Kotlin Multiplatform
0.88 MB−86%
React Native
27.59 MB−7%
.NET MAUI
21.13 MB−43%

Os builds encolhidos não são os que foram medidos, exceto o do MAUI, que não pode ser construído de outro jeito. E a tabela encolhida não pode ser ranqueada como está: o Kotlin Multiplatform não implementa as fases de SQLite, então não leva engine nenhuma, enquanto a da referência é 1,82 MB dos seus 2,66 MB. Subtraindo, os dois ficam a 0,04 MB um do outro.

SQLite: a diferença é a camada de binding

100 000 linhas, o mesmo SQL, o mesmo journal mode e synchronous em todas as plataformas. Os tempos do React Native ficam de fora porque a fase de validação dele falha duas de cinco checagens — que é exatamente para isso que ela existe.

MediçãoCrossFlyFlutterReact Native.NET MAUI
plain insert, 100 000 rows216 ms251 ms—1248 ms
rows/s463 000398 000—80 100
plain bulk insert154 ms232 ms—1242 ms
ciphers runplain, chacha20, sqlcipherplain, sqlcipherplain, sqlcipherplain, sqlcipher
validation11/117/73/57/7
Por que é tão rápido

Menos camadas entre você e a GPU

Cada salto que um quadro faz custa tempo. O CrossFly desenha direto pelo LibGDX — sem ponte JavaScript para atravessar.

CrossFly
2 saltos
Seu código Java
Java puro, sem DSL
Motor LibGDX
Desenho acelerado por GPU
GPU
Pixels na tela
Flutter
2 saltos
Seu código Dart
Nova linguagem para aprender
Motor Skia
Renderiza widgets
GPU
Pixels na tela
React Native
3 saltos + ponte
Seu código JS
Roda em um motor JS
A ponte
Serializada — o gargalo
UI nativa
Views da plataforma
GPU
Pixels na tela

Tudo o que qualifica os números acima

Inteiro, não resumido. Um benchmark sem as ressalvas é um anúncio.

Um celular, uma versão da especificação

Tudo aqui foi medido num Redmi Note 10 Pro (sweet, Adreno 618, 1080×2400, 120 Hz, Android 12), builds de release, 3 iterações por fase e uma abertura do app por iteração. É uma comparação entre cinco frameworks num aparelho, não uma afirmação sobre qualquer outro.

Linhas em 120 fps são pisos, não resultados

A tela roda a 120 Hz, e várias cenas encostam nesse teto em mais de um framework. Uma linha no teto diz "este framework não era o limite aqui" e nada mais; separá-las exige uma carga maior, que é para isso que existe o CFBENCH_LOAD do benchmark.

A taxa de quadros é a do próprio app

O Flashlight conta a janela do app sendo apresentada, o que em quatro das cinco plataformas está desacoplado do custo da cena — ele leu exatamente 60,00 para uma fase do React Native que não desenha nada, e nunca passou de 60,00 em 3388 amostras numa tela de 120 Hz. Então a taxa de quadros é a que cada app mediu de si mesmo; o que o Flashlight acrescenta é CPU e memória, que nenhum app mede sobre si.

CPU não se compara entre plataformas linha a linha

Uma cena a 120 fps envia oito vezes mais quadros que uma a 14, então a CPU dela é maior por um motivo que nada tem a ver com eficiência. Divida pelo número de quadros antes de comparar duas células.

Nada abaixo de uns 3% é um achado

As linhas draw_*, textfield e shapes_text variam esse tanto entre corridas num build inalterado, então uma diferença menor que isso é ruído.

Onde um port não implementa uma fase, ele diz

O Kotlin Multiplatform não tem as fases de SQLite e reporta a lacuna em vez de pulá-la. A validação de SQLite do React Native falha duas de cinco checagens — que é a fase dizendo que os tempos ao lado não são confiáveis — então eles ficam de fora em vez de serem publicados.

Rode você mesmo

O app de benchmark, os quatro ports e os scripts de medição são um repositório só. Cerca de doze minutos por plataforma.

flashlight/run-all.sh flashlight/results/crossfly dev.crossfly.benchmark
python flashlight/summarize.py flashlight/results/crossfly

Números são uma coisa. Rode a coisa.

Trinta e oito exemplos rodam no seu navegador, cada um com o código completo ao lado.

Abrir os exemplosLer a documentação