news 2026/9/13 3:53:12

Compose Multiplatform 跨平台性能基准测试指南:模式、参数与运行脚本全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Compose Multiplatform 跨平台性能基准测试指南:模式、参数与运行脚本全解析

Compose Multiplatform 跨平台性能基准测试指南:模式、参数与运行脚本全解析

【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform

本篇技术指南围绕 Compose Multiplatform 官方仓库中的 benchmarks/multiplatform 基准测试项目展开,系统讲解其在 Desktop、iOS、macOS、Web(Kotlin/Wasm 与 Kotlin/JS)等目标平台上的性能测量机制、四种执行模式(SIMPLE、VSYNC_EMULATION、REAL、STARTUP)的差异与适用场景、完整的配置参数体系,以及三个.main.kts自动化脚本(run_benchmarkscompare_benchmarksfind_degradation)的实战用法。读者读完后,将能够独立配置并运行跨平台基准测试、对比不同 Compose Multiplatform 版本之间的性能差异,并依据源码级证据定位性能回退的具体版本。

基准测试项目概览

Compose Multiplatform 基准测试项目位于仓库的 benchmarks/multiplatform 目录,其核心目标是对 Compose Multiplatform 在不同目标平台上的各类组件与特性进行量化性能测量,覆盖范围包括:

  • 动画(如AnimatedVisibility的显隐切换动画)
  • 懒加载布局LazyVerticalGrid、复杂LazyColumn
  • 文本渲染与排版(大规模文本项的连续滚动排版)
  • 视觉效果(雪花、星星、火箭粒子等复合动画特效)
  • GPU 着色器(多层渐变与混合模式的 Canvas 绘制)

项目采用 Gradle 多模块结构(settings.gradle.kts),由:benchmarks主模块与:compose-scene-api:compose-scene-impl场景实现模块组成。其中:compose-scene-impl会根据当前 Compose Multiplatform 版本自动切换底层场景实现(compose-scene-impl-1/2/3),保证基准测试在不同版本下都能以正确的内部 API 运行,这一点在"版本管理与场景实现适配"一节详述。

所有基准测试代码均位于 benchmarks/src/commonMain/kotlin/benchmarks 下,按功能域分目录组织,且全部使用commonMain共享源码,因此同一份基准测试代码可在所有目标平台上运行。

Benchmark 模式详解

基准测试支持四种执行模式,它们决定了性能的测量方式与报告口径:

SIMPLE(简单模式)

直接测量基础帧耗时,不考虑 VSync 同步。适合快速检查原始渲染性能,例如在持续滚动时粗看帧率是否达标。当未通过modes参数指定任何模式时,SIMPLE 与 VSYNC_EMULATION 会作为默认模式启用

VSYNC_EMULATION(VSync 仿真模式)

模拟 VSync 行为,估算丢帧情况,并给出更贴近真实体验的 CPU/GPU 百分位指标。它不需要真实显示设备,因此在无头(headless)环境中也能运行,适合 CI 流水线中做回归检测。

REAL(真实模式)

在带真实 VSync 的真实场景中运行,能给出用户可感知的最准确结果(FPS、实际丢帧数)。但存在两个局限:其一,如果某一帧耗时恰好落在预算之内,可能捕捉不到性能回退;其二,要求设备具有真实显示器,在无头设备上可能存在问题。

STARTUP(启动性能模式)

专门测量应用启动性能,从进程启动到首帧乃至后续帧输出一系列细粒度时间指标:

指标含义
timeToMain从进程启动到进入应用入口点的时间,平台相关,仅 JVM/Desktop 可用
timeFromMainToFirstFrame从入口点到渲染出第一帧的时间
timeOfFirstFrame第一帧本身的渲染耗时
timeToNthFrame从第一帧到可配置的第 N 帧的时间,默认 N=30
longestFrames启动过程中耗时最长的 N 帧,默认 N=3

模式组合

通过modes参数可以启用一个或多个模式,模式间以逗号分隔:

modes=SIMPLE,VSYNC_EMULATION,REAL,STARTUP

多个模式可以组合执行,例如modes=STARTUP,REAL会先测量启动指标,再运行实时性能测量,最终产出一份统一报告。值得注意的是,从 run_benchmarks.main.kts 的源码可以看到,脚本内部会检测modes参数中是否包含startup,若包含则会自动将separateProcess默认置为true——因为启动性能测量需要进程级隔离才足够准确。

配置参数完整说明

基准测试的配置参数可以通过三种方式传入:

  1. Gradle 任务参数:-PrunArguments="..."(推荐方式)
  2. .main.kts脚本参数:直接作为脚本命令行参数
  3. gradle.properties中的runArguments属性

参数一览表

参数说明示例
modes逗号分隔的执行模式列表(SIMPLEVSYNC_EMULATIONREALSTARTUPmodes=REAL,STARTUP
benchmarks逗号分隔的待运行基准测试列表,可在括号中指定问题规模benchmarks=LazyGrid(100),AnimatedVisibility
disabledBenchmarks逗号分隔的需要跳过的基准测试列表disabledBenchmarks=HeavyShader
warmupCount测量开始前的预热帧数warmupCount=50
frameCount每个基准测试的测量帧数frameCount=500
emptyScreenDelay预热与测量之间的延迟(毫秒),仅 REAL 模式生效emptyScreenDelay=1000
startupFrameCount启动模式下首帧之后要测量的帧数(默认 30)startupFrameCount=50
startupLongestFramesCount启动模式下要报告的耗时最长帧数(默认 3)startupLongestFramesCount=5
parallel是否启用并行渲染,仅 iOS 生效parallel=true
saveStatsToCSV是否将结果保存为 CSV 文件saveStatsToCSV=true
saveStatsToJSON是否将结果保存为 JSON 文件saveStatsToJSON=true
versionInfo在报告中附加版本信息versionInfo=1.2.3
reportAtTheEnd所有基准测试完成后打印汇总报告,仅 REAL 模式生效reportAtTheEnd=true
listBenchmarks列出所有可用基准测试后退出listBenchmarks=true

benchmarks参数支持指定问题规模,例如LazyGrid(100)表示以 100 个条目的规模运行 LazyGrid 基准。这一机制对应源码中不同基准测试的规模参数化设计(详见下文基准测试源码解析)。

命令行用法示例

./gradlew :benchmarks:run -PrunArguments="benchmarks=LazyGrid modes=REAL frameCount=200"

该命令在 Desktop 平台上以 REAL 模式、测量 200 帧的配置运行LazyGrid基准测试。

各平台运行方式

Desktop

Desktop 平台是上手最快的目标,直接运行 Gradle 任务即可:

./gradlew :benchmarks:run

这是基准测试的默认入口,所有支持的运行参数均可通过-PrunArguments传递。

iOS(原生)

在 iOS 上运行有两种途径:

  1. 使用 Fleet 或安装了 KMM 插件的 Android Studio 打开项目,选择iosApp运行配置,务必在 Release 配置下构建应用
  2. 在 Xcode 中打开iosApp/iosApp项目并直接运行。

macOS(原生)

根据处理器架构选择对应的 Gradle 任务:

./gradlew :benchmarks:runReleaseExecutableMacosArm64 # Arm64 处理器 ./gradlew :benchmarks:runReleaseExecutableMacosX64 # Intel 处理器

Web(Kotlin/Wasm 与 Kotlin/JS)

在浏览器中运行前,建议先以**手动 GC(Manual GC)**模式启动浏览器,以获得更干净的测量环境。以 Google Chrome 为例:

open -a Google\ Chrome --args --js-flags="--expose-gc"
  • Kotlin/Wasm./gradlew clean :benchmarks:wasmJsBrowserProductionRun,结果直接打印在页面本身上;
  • Kotlin/JS./gradlew clean :benchmarks:jsBrowserProductionRun,同样在页面打印结果;
  • Wasm 的 D8(V8 引擎)目标./gradlew :benchmarks:wasmJsD8ProductionRun,可携带参数运行,如./gradlew :benchmarks:wasmJsD8ProductionRun -PrunArguments=benchmarks=AnimatedVisibility

如需构建 Jetstream3 风格的 Wasm D8 发行包并直接用 V8 二进制运行:

./gradlew :benchmarks:buildD8Distribution --rerun-tasks # 在发行包目录中执行(路径仅为示例) ~/.gradle/d8/v8-mac-arm64-rel-11.9.85/d8 --module launcher_jetstream3.mjs -- AnimatedVisibility 1000

查看可用基准测试

运行listBenchmarks=true可以列出项目内全部可用的基准测试名称,脚本层(run_benchmarks.main.kts)正是通过解析AVAILABLE_BENCHMARKS_STARTAVAILABLE_BENCHMARKS_END标记行来动态获取该列表,用于separateProcess=true时逐个进程分发执行。

一键运行脚本:run_benchmarks.main.kts

run_benchmarks.main.kts 是跨平台运行基准测试的主入口脚本,统一封装了 macos、desktop、web、ios(源码中还包含 android)四类平台。它处理了大量平台相关细节,例如:为 Web 基准测试启动后台服务器(runServer=true)、为 macOS 将构建产物重命名为带版本号的可执行文件、调用 iOS/Android 专属脚本等。

用法与参数

./run_benchmarks.main.kts <platform> [runs=1] [benchmarks=<benchmarkName>] [version=<version>] [separateProcess=true|false] [any other gradle args]
参数说明
<platform>目标平台:macosdesktopwebios
runs=<number>迭代次数,默认 1
benchmarks=<name1,name2,...>过滤要运行的基准测试
version=<version>指定 Compose Multiplatform 版本;脚本会先改写gradle/libs.versions.toml再运行,结束后自动还原
separateProcess=true每个基准测试在独立进程中运行,隔离性更好(默认false
其他参数直接透传给基准测试执行,例如modes=SIMPLE

示例

./run_benchmarks.main.kts macos benchmarks=LazyList runs=3 version=1.10.0

该命令在 macOS 上以1.10.0版本连续运行 3 轮LazyList基准测试。从源码(run_benchmarks.main.kts)可以确认其版本管理逻辑:脚本会在运行前读取并暂存gradle/libs.versions.toml,通过正则替换compose-multiplatform版本号,全部轮次结束后在finally块中恢复原文件内容,避免污染仓库配置。

结果归档

每轮运行结束后,JSON 结果会被归档到如下目录(源码见 run_benchmarks.main.kts):

benchmarks/build/benchmarks/archive/${platform}/${version}_run${runIndex}

脚本在归档前会先清理benchmarks/build/benchmarks/json-reports目录中上一轮的 JSON 文件,防止旧结果被误归档进新轮次。

版本对比脚本:compare_benchmarks.main.kts

compare_benchmarks.main.kts 用于对比两个 Compose Multiplatform 版本的基准测试结果,是性能回归分析的基础工具。

./compare_benchmarks.main.kts v1=<version1> v2=<version2> [runs=3] [platform=macos|desktop|web|ios] [benchmarks=<name>] [skipExisting=true] [separateProcess=true]

示例:

./compare_benchmarks.main.kts v1=1.9.0 v2=1.10.0 runs=3 platform=macos skipExisting=true

参数说明:

  • v1/v2:待对比的两个版本号;
  • runs:每个版本的运行轮数(默认 3);
  • platform:目标平台;
  • benchmarks:仅对比指定基准测试;
  • skipExisting=true:跳过已经归档过结果的版本,节省重复运行时间;
  • separateProcess=true:每个基准测试独立进程运行。

回归定位脚本:find_degradation.main.kts

find_degradation.main.kts 在版本列表上执行二分搜索,定位首个引入性能回退(定义为耗时增幅 >5%)的版本。

./find_degradation.main.kts benchmarks=<benchmarkName> versions=<versionsFile> [platform=macos|desktop|web] [skipExisting=true]

其中versionsFile是一个纯文本文件,每行一个版本号,且必须按从旧到新的顺序排列。该脚本的核心价值在于:当发现某一指标从版本 A 到版本 B 明显劣化后,可以自动在 A 与 B 之间的所有历史版本中进行二分查找,精确定位回退由哪个版本引入,为代码评审和 bug 追踪提供明确的范围。

iOS 专属脚本

针对 iOS 平台,仓库提供了两套运行工具:

  1. .main.kts脚本./run_ios_benchmarks.main.kts <DEVICE ID>,支持与其它目标平台完全一致的参数配置方式(脚本参数或gradle.properties中的runArguments属性);
  2. Shell 脚本:iosApp/run_ios_benchmarks.sh<DEVICE ID>,专门支持real模式下多次尝试运行的场景。

准备步骤

  • 若要在真机运行,先打开iosApp/iosApp.xcodeproj,在Signing & Capabilities项目页签中正确配置签名(Signing)部分;
  • 使用xcrun xctrace list devices获取全部 iOS 设备 ID 列表。

常用命令

# 从 benchmarks 目录运行指定设备上的全部基准测试 ./run_ios_benchmarks.main.kts <DEVICE ID> # 只运行指定的基准测试 ./run_ios_benchmarks.main.kts <DEVICE ID> benchmarks=AnimatedVisibility,LazyGrid # 每个基准测试独立进程运行(较慢但更可靠,避免同进程内相互干扰) ./run_ios_benchmarks.main.kts <DEVICE ID> separateProcess=true

结果输出位置

  • 使用.main.kts脚本:结果保存于benchmarks/build/benchmarks/text-reports/
  • 使用.sh脚本:结果保存于benchmarks_result/

内置基准测试与源码解析

项目内置了 9 组基准测试,全部位于 benchmarks/src/commonMain/kotlin/benchmarks,共享同一份commonMain代码,因此测试场景在任何平台完全一致,保证了跨平台对比的可信度。

基准测试名称源码文件测试内容
AnimatedVisibilityanimation/AnimatedVisibility.kt反复切换 PNG 图片的可见性,测试AnimatedVisibility组件性能
LazyGridlazygrid/LazyGrid.kt12000 条目的LazyVerticalGrid,运行中多次跳转到指定条目
LazyGrid-ItemLaunchedEffect同上同 LazyGrid,但每个条目额外带一个模拟异步任务的LaunchedEffect
LazyGrid-SmoothScroll同上同 LazyGrid,但改用平滑滚动而非跳转
LazyGrid-SmoothScroll-ItemLaunchedEffect同上组合了平滑滚动与条目内LaunchedEffect
VisualEffectsvisualeffects/HappyNY.kt复杂动画与视觉特效:雪花、星星、火箭粒子
LazyListcomplexlazylist/components/MainUI.kt复杂LazyColumn:下拉刷新、加载更多、持续滚动
MultipleComponentsmultipleComponents/MultipleComponents.kt综合 UI:布局、动画、样式化文本等大量组件同屏展示
MultipleComponents-NoVectorGraphics同上同 MultipleComponents,但跳过矢量图形渲染的 Composable
TextLayouttextlayout/TextLayout.kt连续滚动包含大量重排版条目的列,测试文本布局与渲染性能
CanvasDrawingcanvasdrawing/CanvasDrawing.kt滚动包含海量图形形状的条目,测试 Canvas 绘制性能
HeavyShaderheavyshader/HeavyShader.kt滚动包含复杂 GPU 着色器的条目,测试 GPU 着色器性能

源码级实现细节

LazyGrid 系列(LazyGrid.kt):固定 4 列(GridCells.Fixed(4)),预生成 12000 个条目。跳转模式下,每帧以 50 为步长执行state.scrollToItem(curItem),在列表头尾之间往返;平滑滚动模式下改为每帧state.scrollBy(55f)(或 -55f 反向)。ItemLaunchedEffect变体在每个条目内启动一个LaunchedEffect,其协程体执行suspendCoroutine { }挂起后永不恢复,用于模拟条目作用域中存在长驻异步任务时的开销。

VisualEffects(HappyNY.kt):场景包含 80 片雪花(snowCount)、60 颗星星(starCount)与 30 个火箭粒子(rocketPartsCount)。雪花带有重力、正弦摆动与旋转相位,星星为带旋转的十字形,火箭采用两级爆炸模型——主火箭减速后分裂为 7 枚子火箭,每枚再爆裂为 30 个带拖尾渐隐的粒子,整体以withFrameNanos驱动逐帧状态更新。

TextLayout(TextLayout.kt):共 2000 个条目,每个条目内嵌套 12 行 × 10 列的网格,每个单元格均含小字号Text(6.sp),配合每帧scrollBy(67f)的持续滚动,形成高密度的文本测量、排版与重绘压力。

CanvasDrawing(CanvasDrawing.kt):1200 个条目,每个条目 300.dp 高的 Canvas 内绘制 200 条渐变路径(含quadraticBezierTo贝塞尔曲线)、70 条摆动线条与 70 个脉动圆,全部由无限动画驱动,重点考验 Canvas 图元与渐变渲染吞吐。

HeavyShader(HeavyShader.kt):800 个条目,每个 Canvas 内叠加 60 层径向渐变矩形(layers = 60),逐层使用ScreenOverlayMultiply三种BlendMode轮换混合,另附 60 个BlendMode.Plus的光晕圆,对 GPU 像素填充率与混合单元构成重度负载。

LazyList(MainUI.kt):复刻真实社交信息流场景,基于SwipeRefreshLayout实现下拉刷新(delay(2000)模拟网络请求后重建数据)与上滑加载更多,同时内部以scrollBy(20f)保持持续滚动,覆盖了含嵌套滚动手势的复杂懒加载列表性能。

MultipleComponents(MultipleComponents.kt):几乎集齐 Material 组件全家桶——TopAppBarExtendedFloatingActionButtonBottomAppBarSwitchCheckboxSliderDropdownMenuTextFieldCircularProgressIndicator,以及富文本(buildAnnotatedString+ 内联Placeholder)、BrushTextGradient/BrushTextImage渐变文本、10 万条目的右侧LazyColumn等。MultipleComponents-NoVectorGraphics变体通过isVectorGraphicsSupported = false跳过ImageIcon等矢量资源绘制,用于分离矢量渲染在总开销中的占比。

结果输出与格式

基准测试结果支持三种输出通道,可自由组合:

  • JSONsaveStatsToJSON=true):结构化数据,供脚本归档与版本对比使用,归档路径为benchmarks/build/benchmarks/archive/${platform}/${version}_run${runIndex}
  • CSVsaveStatsToCSV=true):表格化数据,便于在电子表格工具中做进一步分析;
  • 文本报告:iOS 平台经.main.kts脚本运行时输出到benchmarks/build/benchmarks/text-reports/,经.sh脚本运行时输出到benchmarks_result/

版本管理与场景实现适配

从 settings.gradle.kts 的源码可以看出,该基准测试项目对 Compose Multiplatform 版本变化做了精细的适配设计:

  • 版本解析:项目内置 Semver 解析器(支持major.minor.patch[-preRelease][+buildMetadata]格式),并允许通过 Gradle 属性compose.version覆盖gradle/libs.versions.toml中声明的版本;
  • 场景实现分代:按 Compose Multiplatform 内部场景 API 的演进设置了两个分界点(barrier):
    • compose-scene-impl-1:基线版本,使用ComposeScene.render(canvas, nanoTime)旧 API;
    • compose-scene-impl-2:从 1.12.0-alpha02(dev4213 起)开始,改用FrameRecomposer.performFrame()+ComposeScene.measureAndLayout()+ComposeScene.draw(canvas)拆分 API;
    • compose-scene-impl-3:从 1.12.10-alpha01(dev4534 起)开始,额外要求运行期调用registerSkikoComposeImplementation()注册 Skiko 图形与文本实现;
  • 自包含复制:每个实现模块都是前一个模块的完整自包含副本,刻意保留重复代码,以确保适配新 API 时绝不破坏旧版本仍能构建。

这一机制保证了"用旧版本跑基准"与"用新版本跑基准"都能正确编译并链接到与之匹配的场景实现,是version=参数与compare_benchmarks/find_degradation脚本能够跨版本对比的前提。

快速上手建议

  1. 首次体验:直接运行./gradlew :benchmarks:run,使用默认的SIMPLE+VSYNC_EMULATION模式快速验证桌面端渲染性能;
  2. 探索基准集:先执行./gradlew :benchmarks:run -PrunArguments=listBenchmarks=true查看全部可用基准测试;
  3. 针对性测量:使用benchmarks=LazyGrid modes=REAL frameCount=200组合参数聚焦单个场景,REAL 模式需在带真实显示器的设备上运行;
  4. 跨版本对比:需要评估升级影响时,用./compare_benchmarks.main.kts v1=<旧版本> v2=<新版本> platform=macos一键对比;
  5. 定位回退:发现劣化后,准备按从旧到新排序的版本清单文件,用./find_degradation.main.kts benchmarks=<名称> versions=<版本文件>二分定位引入回退的版本;
  6. CI 集成:无头环境建议使用VSYNC_EMULATION模式并配合saveStatsToJSON=true,将 JSON 结果归档到统一目录做持续回归监测。

在阅读源码时,建议以 benchmarks/src/commonMain/kotlin/benchmarks 下的各基准测试实现为入口,结合 run_benchmarks.main.kts 了解脚本编排逻辑,再对照 settings.gradle.kts 理解版本适配机制,即可完整掌握这套跨平台性能基准体系的运行原理与扩展方式。

【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 3:53:08

Ubuntu 20.04安装Docker完整指南:避坑、配置与存储迁移

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 3:50:00

压力测试实战全解析:JMeter压测、指标解读与性能问题定位

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 3:48:19

PolarDB-X分布式JOIN性能实测:Broadcast与Shard策略选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华