1. 课程导入与性能工程全景

Android Native 性能工程,指的是在 Android 应用中由 C/C++(含 Rust、汇编)代码承担关键路径时,围绕启动、流畅、内存、功耗、包体五类核心指标,对 Native 层进行可测量、可归因、可回归的工程化优化。它与 Java/Kotlin 层性能优化最大的区别在于:Native 层没有 ART 的托管运行时兜底,内存要自己管、线程要自己调度、崩溃往往是 SIGSEGV 而非可捕获异常,因此“先建立观测能力,再谈优化”几乎是唯一可行的路径。

一、Native 性能的边界在哪里

很多初学者会把“Native 性能”等同于“把代码用 C++ 重写一遍”。这是典型的边界误判。判断一段逻辑是否值得下沉到 Native,通常看三个条件:

  • 计算密度高:单位数据上的运算量大,例如图像滤镜、音视频编解码、物理模拟、加解密、大矩阵运算。
  • 调用频次低但单次耗时长:JNI 跨语言调用本身有开销(一次普通 JNI 调用约几十到几百纳秒,涉及对象转换时更高),高频小调用下沉反而更慢。
  • 需要复用已有 C/C++ 库:如 FFmpeg、OpenCV、Skia、SQLite、TensorFlow Lite,重写成本远高于桥接成本。

反过来,UI 布局、列表滚动、简单字符串处理、业务状态机这类逻辑,留在 Kotlin 层由 ART 优化更划算。Native 性能工程的第一课,其实是克制:只在收益明确的地方下沉。

二、五类核心指标与它们的观测入口

性能指标不是抽象口号,每一个都对应具体的采集手段和判定阈值。下面这张图把五类指标、典型 Native 诱因和常用观测工具串成一条全景链路,便于建立整体认知。

Android Native 性能工程全景 指标 → 典型 Native 诱因 → 观测工具 → 优化方向 启动 冷启 / 首帧 / 首屏 流畅 帧率 / 卡顿 / 掉帧 内存 RSS / 泄漏 / 抖动 功耗 CPU / 唤醒 / 温升 包体 .so / 符号 / 资源 JNI_OnLoad 阻塞、静态初始化 大量 .so 加载、符号重定位 主线程同步 JNI、锁竞争 渲染线程与计算线程争抢 CPU malloc 碎片、未释放的 native 对象 Bitmap / 解码缓冲区膨胀 忙轮询、无节制线程池 高频定时器、后台计算不停 未 strip 的调试符号、多 ABI 静态链接重复库、LTO 缺失 Perfetto / systrace simpleperf、atrace Perfetto 帧时间线 gfxinfo、Choreographer malloc_debug、ASan heapprofd、meminfo Battery Historian powermetrics、top APK Analyzer、nm/readelf strip、objcopy、LTO 延迟加载 / 异步初始化 合并 .so、预链接 减少跨线程同步 计算移出主线程 对象池 / 复用缓冲 及时释放、避免碎片 按需唤醒 / 批处理 降低 CPU 占用时长 strip + 单 ABI + LTO 裁剪符号与重复库 核心指标 典型 Native 诱因 观测工具 优化方向

以启动为例:冷启动从进程 fork 到首帧上屏,Native 侧常见耗时点是 JNI_OnLoad 中的静态注册、System.loadLibrary 触发的 .so 映射与重定位、以及 C++ 全局对象的构造函数。用 simpleperf record -g --app com.example.app 抓取启动阶段调用栈,往往能看到 dlopen、__linker_init、_init 占据可观比例。判定标准可以设为:冷启动 P90 超过 1.5s 即进入优化队列,其中 Native 初始化占比超过 15% 就值得单独治理。

三、典型问题域与归因思路

Native 性能问题大致落在四个域:加载域(.so 体积、符号、重定位)、调用域(JNI 开销、线程切换、锁)、计算域(算法复杂度、SIMD 利用、缓存局部性)、资源域(内存分配、文件 IO、GPU 上传)。归因时遵循“先定位域,再定位函数,最后定位行”的收敛顺序,避免一上来就逐行读代码。

一个可复用的判断路径是:先用 Perfetto 看时间线上是“主线程被阻塞”还是“计算本身慢”。如果是主线程阻塞,重点查同步 JNI 与锁;如果是计算慢,用 simpleperf 采样热点函数,再看是否能用 NEON/SIMD 或算法替换。例如一段 1080p RGBA 图像逐像素灰度化,纯 C 循环在中等机型上约 18~25ms,改用 NEON 一次处理 8 个像素后通常可降到 4~7ms(示例量级,实际随机型与编译选项变化)。

四、学习路线图

本课程按“观测 → 归因 → 优化 → 回归”的能力递进组织,建议按下面的顺序推进,不要跳步:

  1. 建立观测能力:掌握 Perfetto、simpleperf、malloc_debug、APK Analyzer 的基本用法,能独立抓取一份可分析的 trace。
  2. 理解 Native 运行时:.so 加载与链接、JNI 调用约定、线程与内存模型,这是后续所有优化的地基。
  3. 分指标专项:启动、流畅、内存、功耗、包体各自成体系,逐个击破。
  4. 工程化回归:把指标接入 CI,用基准测试防止性能回退。
学习本课程时,建议准备一台可 root 或可刷 userdebug 的设备,并固定一个基准测试场景(例如“冷启动到首页可交互”)。所有优化前后对比都在同一场景、同一设备、同一编译配置下进行,否则数据没有可比性。
不要用 Debug 包做性能结论。Debug 构建通常关闭优化(-O0)、保留完整符号、开启断言,Native 计算可能比 Release 慢 3~10 倍。所有性能数据必须来自 Release 或 Profile 构建。

最后强调一个贯穿全课的原则:没有测量就没有优化。Native 性能工程的价值不在于写出多“高级”的 C++,而在于能把一个模糊的“卡”拆解成可量化、可归因、可验证的工程问题。接下来的章节,会从观测工具开始,一步步把这套能力建立起来。