跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用
基准测试

五个框架,同样的工作量, 同一部手机

十四个阶段,逐场景在 Flutter、React Native、.NET MAUI 和 Kotlin Multiplatform 中重新实现,全程无需点击。本页每个数字都来自真实设备,没有一个是挑出来的。

测试设备 Redmi Note 10 Prosweet, Adreno 618, 1080×2400, 120 Hz, Android 123 每阶段迭代次数
CrossFly CoreCrossFly Sync

请先读这里

没有这些,下面的表格是读不懂的。它们来自基准测试自己的文档,不是我们的说法。

一部手机,一个规格版本

这里的一切都测自一部 Redmi Note 10 Pro(sweet,Adreno 618,1080×2400,120 Hz,Android 12),release 构建,每阶段 3 次迭代,每次迭代启动一次应用。这是五个框架在同一台设备上的比较,不是对其他任何设备的断言。

120 fps 的行是下限,不是结果

屏幕以 120 Hz 运行,多个场景在不止一个框架上触及该上限。触顶的行只说明"该框架在这里不是瓶颈",仅此而已;要区分它们需要更重的负载,这正是基准测试的 CFBENCH_LOAD 的用途。

帧率是应用自己测的

Flashlight 统计的是应用窗口的呈现,在五个平台中的四个上这与场景开销无关——它为一个什么都不画的 React Native 阶段读出了恰好 60.00,并且在 120 Hz 屏幕上的 3388 次采样中从未超过 60.00。所以帧率是每个应用对自身的测量;Flashlight 提供的是 CPU 和内存,这是任何应用都无法自测的。

逐阶段帧率

每个应用对自身的测量——五个平台中有四个,外部性能分析器测的是窗口而不是场景。越高越好。

阶段CrossFlyFlutterReact 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.8触顶49.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.5触顶53.972.6119.7触顶120.0触顶
shapes_lines
a batched line every 1px across the surface
111.429.3120.1触顶119.8触顶119.9触顶
shapes_circles
50 concentric filled circles
99.140.9107.3112.9105.1
shapes_polygon_concave
3 nested 36-point concave stars
118.6触顶119.2触顶89.0117.287.4
shapes_polygon_convex
3 nested 36-side convex polygons
119.6触顶119.3触顶120.0触顶120.0触顶120.0触顶
shapes_shadow
8 nested rounded rects with a 40px blurred shadow
119.9触顶110.2119.4触顶120.0触顶120.0触顶
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 pass未实现7/7 pass
sqlite_bench
100k rows × {plain, ChaCha20, SQLCipher}
3 ciphers2 ciphers2 ciphers未实现2 ciphers

带底色的单元格已达到屏幕 120 Hz 上限。它们只说明“该框架在这里不是瓶颈”,仅此而已——是下限,不是结果。

有一行是移植版赢了

shapes_circles —— 50 个同心实心圆,约 3500 万像素的不透明重绘 —— 在四个移植版中的三个上跑得更快。两边做的是同样的工作:它们都是这块 GPU 的 OpenGL ES 驱动上的 Skia,其帧时间与绘制面积成线性关系,和参考实现完全一样,区别在于每像素的代价。在这里参考实现受限于自己的三角剖分,是 CPU 瓶颈。

内存才是各框架真正拉开差距的地方

十四个阶段的 PSS 峰值。下限是一个框架在画任何东西之前就要收的费用;上限是它最糟糕的场景的代价。

CrossFly118–135 MB · 2400 个文本框 132 MB
下限上限
Kotlin Multiplatform121–227 MB · 2400 个文本框 341 MB
下限上限
React Native126–591 MB · 2400 个文本框 601 MB
下限上限
Flutter152–286 MB · 2400 个文本框 556 MB
下限上限
.NET MAUI174–203 MB · 2400 个文本框 321 MB
下限上限

下限就是运行时加上 GL 上下文——在第一个像素之前就要付的门票钱。

唯一一个所有移植版都付出代价而参考实现没有的场景

2400 个原生文本框 —— EditText、TextInput、Entry、BasicTextField —— 要花 321 到 601 MB。CrossFly 自己绘制这 2400 个,只花 132 MB,比它自身的下限高 14 MB。代价不在于绘制文本框,而在于把它们作为平台对象持有。

最终交付物

Release、已签名、仅 arm64-v8a —— 否则比较的是打包惯例,而不是框架。

未压缩 —— 关闭 R8
CrossFly
5.95 MB
Flutter
22.65 MB
Kotlin Multiplatform
6.17 MB
React Native
29.69 MB
.NET MAUI
37.34 MB
已压缩 —— 只保留各应用实际用到的代码
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%

压缩后的构建并不是被测量的那些,MAUI 除外——它只能这样构建。而且压缩表按现状无法排名:Kotlin Multiplatform 没有实现 SQLite 阶段,因此不携带任何引擎,而参考实现的引擎占其 2.66 MB 中的 1.82 MB。减掉它,两者相差 0.04 MB。

SQLite:差距在绑定层

100 000 行,所有平台使用相同 SQL、相同 journal mode 与 synchronous 设置。React Native 的耗时被排除,因为它自己的验证阶段有两项未通过——那正是该阶段存在的意义。

测量项CrossFlyFlutterReact 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
为何如此之快

你与 GPU 之间的层级更少

每一帧多走一跳都要花时间。CrossFly 通过 LibGDX 直接绘制——没有 JavaScript 桥要跨越。

CrossFly
2 跳
你的 Java 代码
纯 Java,无 DSL
LibGDX 引擎
GPU 加速绘制
GPU
像素上屏
Flutter
2 跳
你的 Dart 代码
要学的新语言
Skia 引擎
渲染控件
GPU
像素上屏
React Native
3 跳 + 桥
你的 JS 代码
运行在 JS 引擎中
桥
序列化——瓶颈所在
原生 UI
平台视图
GPU
像素上屏

上述数字的全部限定条件

完整保留,不做概括。没有限定条件的基准测试就是广告。

一部手机,一个规格版本

这里的一切都测自一部 Redmi Note 10 Pro(sweet,Adreno 618,1080×2400,120 Hz,Android 12),release 构建,每阶段 3 次迭代,每次迭代启动一次应用。这是五个框架在同一台设备上的比较,不是对其他任何设备的断言。

120 fps 的行是下限,不是结果

屏幕以 120 Hz 运行,多个场景在不止一个框架上触及该上限。触顶的行只说明"该框架在这里不是瓶颈",仅此而已;要区分它们需要更重的负载,这正是基准测试的 CFBENCH_LOAD 的用途。

帧率是应用自己测的

Flashlight 统计的是应用窗口的呈现,在五个平台中的四个上这与场景开销无关——它为一个什么都不画的 React Native 阶段读出了恰好 60.00,并且在 120 Hz 屏幕上的 3388 次采样中从未超过 60.00。所以帧率是每个应用对自身的测量;Flashlight 提供的是 CPU 和内存,这是任何应用都无法自测的。

CPU 不能跨平台逐行比较

以 120 fps 运行的场景提交的帧数是 14 fps 场景的八倍,因此它的 CPU 更高,原因与效率毫无关系。比较两个单元格之前,先除以帧数。

低于约 3% 的差异不算发现

在未改动的构建上,draw_*、textfield 和 shapes_text 各行在不同轮次间就会有这么多漂移,因此小于该幅度的差异是噪声。

移植版未实现某阶段时,它会明说

Kotlin Multiplatform 没有 SQLite 阶段,它报告这一缺口而不是跳过。React Native 的 SQLite 验证有两项未通过——那正是该阶段在说旁边的耗时不可信——所以这些耗时被排除而非发布。

自己跑一遍

基准应用、四个移植版和测量脚本都在同一个仓库里。每个平台大约十二分钟。

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

数字是一回事。把它跑起来。

三十八个示例在你的浏览器中运行,每个旁边都有完整源码。

打开示例阅读文档