启动优化-减少动态库

动态库加载是Pre-main阶段的重要组成部分,每个动态库都需要加载、验证签名、进行符号绑定,这些操作会显著影响启动时间。 问题分析 当App启动时,dyld需要: 分析Mach-O文件的Load Commands,找出依赖的动态库 递归加载所有依赖的动态库 对每个动态库进行签名验证 执行Rebase和Bind操作 动态库数量越多,这些操作的耗时就越长。Apple建议将自定义动态库数量控制在6个以内。关于动态库加载的详细流程,可以参考Mach-O的链接、装载与库。 关于缓存机制: 系统动态库:已被放入 dyld shared cache 中,其加载和符号绑定操作已预先完成,不会影响 App 启动时间 App 动态库:dyld 3 引入的 Launch Closure 机制会缓存依赖分析、Rebase/Bind 信息等元数据。首次启动(或 App 更新后)会生成缓存,后续启动直接使用 但即使有 Launch Closure 缓存,Rebase/Bind 操作本身仍需执行(因为 ASLR slide 每次启动都不同),动态库数量越多,这些操作的耗时仍然越长。 App可执行文件 ├── UIKit.framework │ ├── Foundation.framework │ │ └── CoreFoundation.framework │ └── CoreGraphics.framework ├── 自定义Framework A │ └── 依赖库... └── 自定义Framework B └── 依赖库... 优化方案 1. 合并动态库 将功能相近的动态库合并为一个: ...

May 2, 2026

启动优化-观测

准确测量启动时间是优化的前提。启动时间是用户体验的关键指标之一,研究表明,应用性能直接影响用户留存:页面加载时间每增加1秒,用户流失率会显著上升。对于iOS应用,需要在20秒内完成启动,否则可能会被watchdog强制终止。 本文介绍如何测量和监控iOS应用的启动时间。关于启动流程的详细介绍,请参考 App启动流程。 一、测量方式概览 iOS 提供了多种测量启动时间的方式,适用于不同场景: 方式 适用场景 粒度 是否需要代码 dyld 环境变量 开发调试 定性分析 否 Instruments App Launch 深度分析 函数级 否 代码埋点 开发调试 + 线上监控 自定义 是 MetricKit 线上监控 冷启动/热启动 少量代码 二、开发调试阶段 2.1 dyld 环境变量 通过设置 dyld 环境变量可以在控制台查看启动过程中的加载信息: Edit Scheme → Run → Arguments → Environment Variables 与启动测量相关的环境变量: 环境变量 作用 启动分析用途 DYLD_PRINT_LIBRARIES 打印每个加载的 mach-o 镜像 查看动态库加载顺序,确认是否加载了不必要的库 DYLD_PRINT_LOADERS 打印镜像的加载器类型(JustInTimeLoader / PrebuiltLoader) 分析是否使用了启动闭包优化 DYLD_PRINT_INITIALIZERS 打印每个 initializer 的执行 查看 +load、constructor、C++ 静态构造函数的执行顺序 DYLD_PRINT_BINDINGS 打印每次符号绑定 分析符号绑定情况(输出量很大) DYLD_PRINT_SEARCHING 打印库搜索路径 排查库加载问题 DYLD_PRINT_APIS 打印 dyld API 调用(如 dlopen) 检测运行时动态加载行为 DYLD_PRINT_TO_FILE 将日志输出到指定文件 避免控制台日志过多,方便后续分析 使用示例: ...

May 2, 2026

启动优化

iOS应用的启动时间是用户体验的关键指标之一。研究表明,应用性能直接影响用户留存:页面加载时间每增加1秒,用户流失率会显著上升。对于iOS应用,需要在20秒内完成启动,否则可能会被watchdog强制终止。 本系列文章从启动流程的各个阶段入手,系统性地介绍iOS启动优化的方法和实践。 启动监控是 APM 的重要子系,线上 P50/P90/P99 的采集、冷/温/热启动判定、以及与业务秒开指标的关联,请参考 APM 系列:指标体系、数据采集。 启动流程概述 App的启动流程分为两个主要阶段: 阶段 时间范围 主要工作 Pre-main 进程创建 → main()函数 加载可执行文件、加载动态库、Rebase/Bind、ObjC Runtime初始化、Swift Runtime元数据注册、+load方法、Initializers main main()函数 → 首帧渲染 UIApplicationMain、AppDelegate回调、首帧渲染 flowchart TD Start([App 冷启动]) --> PreMain subgraph PreMain["Pre-main 阶段"] direction TB A[加载可执行文件创建进程、mmap映射、签名验证] --> B[加载动态库递归加载依赖、包括 Swift Runtime] B --> C[Rebase & Bind指针修正、符号绑定] C --> D[ObjC Runtime 初始化类注册、Category 附加] D --> E[Swift Runtime 元数据注册类型元数据、协议遵循表] E --> F[调用 +load 方法] F --> G[执行 InitializersC++静态构造函数、__attribute__] end PreMain --> MainPhase subgraph MainPhase["main 阶段"] direction TB H[main函数] --> I[UIApplicationMain创建UIApplication、AppDelegate、启动RunLoop] I --> J[AppDelegate回调willFinish、didFinishLaunching] J --> K[首帧渲染创建Window、RootViewController、首屏UI] end MainPhase --> End([启动完成]) 文章导航 本系列包含以下文章,建议按顺序阅读: ...

May 2, 2026

耗电-CPU与后台优化

CPU是App最容易"无意识"耗电的模块,而后台则是最容易"偷偷"耗电的场景。本文聚焦这两大问题域,给出具体的优化手段。 一、CPU耗电的根本原因 根据 耗电-原理 中的分析,CPU耗电由三部分构成: 活跃时间:CPU处于C0/P0的时间。 瞬时功率:和P-State频率相关。 唤醒次数:频繁从C-State唤醒无法进入深度休眠。 对应三条优化方向: 优化方向 策略 减少活跃 任务及时结束,该停就停 降低功率 避免无谓的满核满频,使用合适的QoS 合并唤醒 聚合Timer、聚合任务,让CPU能连续工作后进入深休眠 二、识别CPU耗电热点 从MetricKit入手 cumulativeCPUTime 是线上最直接的CPU指标。对比同版本前后、同机型前后的CPU时长,能快速定位回归。 Time Profiler的使用技巧 在Instruments中抓一份Time Profiler Trace后: 按"Heavy Stack"视图查看耗时函数。 关注后台的采样(通过Xcode的生命周期切换复现)。 查看线程调用频率——单次耗时低但高频调用的函数,同样是耗电大头。 代码层面的CPU异味 // 1. 死循环轮询 while isWaitingForData { if dataReady { break } } // 2. 主线程sleep循环 DispatchQueue.global().async { while true { checkStatus() Thread.sleep(forTimeInterval: 0.1) } } // 3. 密集Timer Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { _ in self.updateUI() } // 4. 无限制的CADisplayLink displayLink = CADisplayLink(target: self, selector: #selector(tick)) displayLink.add(to: .main, forMode: .common) // 页面隐藏后忘记invalidate 这些代码在Time Profiler中都会显示出异常高的调用频率。 ...

May 2, 2026

耗电-原理

本文从硬件功耗模型、iOS系统的能效调度机制、以及各个硬件模块的功耗特性三个维度,系统性地讲解iOS App耗电的底层原理。 功耗的基本概念 功率、能量与电量 功率(Power):单位时间内消耗的能量,单位为瓦特(W)。 能量(Energy):功率在时间上的积分,单位为焦耳(J)或毫瓦时(mWh)。 电量(Battery Capacity):电池能存储的总能量,单位为毫安时(mAh)。 \[ Energy = \int P(t)\,dt \]对于一段时间内近似恒定的功率,可以简化为 \( E = P \times t \)。这也是所有耗电优化的起点——要么降功率(P),要么降时间(t)。 电池电压与电流 iPhone使用锂电池,标称电压约为3.7~3.8V。操作系统看到的电流则随着硬件负载实时变化。可以近似认为: \[ P(t) = V \times I(t) \]在App层面,我们无法直接测量V和I,但可以通过 UIDevice.current.batteryLevel 观察电量百分比,通过 IOKit 私有API读取更精细的电池状态(后面检测篇会详细讲)。 硬件功耗特性 不同硬件模块的功耗曲线差异非常大。理解每个模块的特性,才能找到最有效的优化点。 CPU:P-State与C-State 现代CPU(包括Apple Silicon)通过 动态电压频率调整(DVFS) 来平衡性能与功耗。 P-State(Performance State) P-State描述CPU在工作时的电压/频率档位。以Apple A系列芯片为例,大核和小核都有多个P-State: flowchart LR subgraph P-State P0["P0最高频率功耗最大"] P1["P1中高频率"] P2["P2中低频率"] P3["P3低频率功耗小"] end P0 -.->|负载下降| P1 P1 -.-> P2 P2 -.-> P3 P3 -.->|负载上升| P0 CPU的功耗与频率大致呈 立方关系(电压随频率升高,功耗 ≈ V² × f ≈ f³)。这意味着: ...

May 2, 2026

耗电-定位与传感器优化

定位是iOS设备上公认的"耗电大户",GPS冷启动一次动辄十几秒的高功耗;传感器(加速度计、陀螺仪等)虽然单次功耗不高,但高频采样会累积成可观的电量消耗;屏幕则是 持续 耗电的模块,其功耗和亮度、刷新率、像素颜色都强相关。本文聚焦这三类问题,给出工程化的优化方案。 一、定位精度与功耗的关系 iOS通过 CLLocationManager 提供定位服务,底层融合了 GPS、蜂窝基站、WiFi、蓝牙信标 等多源信号。精度越高,需要开启的硬件越多,功耗越高。 精度常量 精度范围 硬件使用 典型功耗 kCLLocationAccuracyBestForNavigation <5m GPS + 传感器融合(需插电) 极高 kCLLocationAccuracyBest ~10m GPS持续 高 kCLLocationAccuracyNearestTenMeters ~10m GPS + WiFi 高 kCLLocationAccuracyHundredMeters ~100m WiFi + 基站 中 kCLLocationAccuracyKilometer ~1km WiFi + 基站(低频) 低 kCLLocationAccuracyThreeKilometers ~3km 基站 极低 注:iOS 14+ 引入的 ReducedAccuracy 是用户授权层面的"粗略位置",通过 CLAccuracyAuthorization.reducedAccuracy 体现,而不是 desiredAccuracy 常量。即使 App 申请了高精度,只要用户选择了"粗略位置",系统就只会返回约5公里精度的数据,功耗也随之降到极低。 不同业务场景的精度选择 业务 推荐精度 导航 BestForNavigation(仅使用中 + 插电时) 打车、骑行记录 Best 外卖、附近的人 HundredMeters 天气、资讯推荐 Kilometer 或 ThreeKilometers 反欺诈、粗粒度风控 ReducedAccuracy(尊重隐私 + 省电) 常见反例 // Bad: 所有场景都用Best,页面打开就开,关闭不关 locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.startUpdatingLocation() // Bad: distanceFilter用kCLDistanceFilterNone,每次都回调 locationManager.distanceFilter = kCLDistanceFilterNone 正确姿势:按需精度 + 距离过滤 class WeatherLocationClient: NSObject, CLLocationManagerDelegate { private let manager = CLLocationManager() private var completion: ((CLLocation) -> Void)? override init() { super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyKilometer manager.distanceFilter = 500 // 500米才更新 } func requestOnce(_ completion: @escaping (CLLocation) -> Void) { self.completion = completion manager.requestLocation() // 一次性定位,完成后自动停止 } func locationManager(_ manager: CLLocationManager, didUpdateLocations locs: [CLLocation]) { guard let loc = locs.last else { return } completion?(loc) completion = nil } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { completion = nil } } requestLocation()(iOS 9+)是最省电的一次性定位入口,系统会在获取到满足精度的定位后自动停止硬件。 ...

May 2, 2026

耗电-检测

本文介绍iOS耗电问题的检测手段,从开发阶段的本地工具,到线上的监控体系,帮助团队形成一套完整的耗电可观测能力。 检测手段全景 flowchart TB subgraph Dev["研发阶段"] EG["Xcode Energy Gauge实时能效评分"] Inst["InstrumentsEnergy Log / Time Profiler"] XCT["XCTest能效测试"] end subgraph Test["测试阶段"] Thermal["热节流测试"] Battery["真机电量消耗测试"] IOKit["IOKit内部分析"] LPM["低电量模式回归"] end subgraph Online["线上阶段"] MK["MetricKit"] Self["自建实时采集电量 / CPU / Thermal / 生命周期"] Report["埋点上报数据看板"] end Dev --> Test --> Online 一、Xcode Energy Gauge Xcode在真机调试时,“Debug Navigator → Energy Impact"提供了实时能效评估。 能效评分(Energy Impact) Xcode将功耗分为5档: 等级 含义 典型值 Very Low / Low 正常使用 03 / 48 High 偏高,需要关注 9~16 Very High 严重,必须优化 17~20 评分由以下几个因素构成: CPU:平均CPU使用率。 Network:网络活跃时间与流量。 Location:定位活跃时间与精度。 GPU:GPU活跃时间。 Background:后台活跃时间。 Overhead:系统唤醒代价。 典型使用流程 Xcode连接真机(模拟器的数据不准确)。 运行App,选择Debug Navigator → Energy Impact。 进行典型操作(刷Feed、看视频、聊天等),观察评分。 如果出现High/Very High,使用Instruments进一步定位。 二、Instruments Energy Log Instruments中有多个和能效相关的工具: ...

May 8, 2026

性能优化

本文篇幅较长,将从 编译阶段 -> 路由阶段 -> 渲染阶段 -> 细节优化 -> 状态管理 -> 海量数据源,长列表渲染 方向分别加以探讨。 一 不能输在起跑线上,优化babel配置,webpack配置为项 1 真实项目中痛点 当我们用create-react-app或者webpack构建react工程的时候,有没有想过一个问题,我们的配置能否让我们的项目更快的构建速度,更小的项目体积,更简洁清晰的项目结构。 随着我们的项目越做越大,项目依赖越来越多,项目结构越来越来复杂,项目体积就会越来越大,构建时间越来越长,久而久之就会成了一个又大又重的项目,所以说我们要学会适当的为项目‘减负’,让项目不能输在起跑线上。 2 一个老项目 拿我们之前接触过的一个react老项目为例。我们没有用dva,umi快速搭建react,而是用react老版本脚手架构建的,这对这种老的react项目,上述的问题都会存在,下面让我们一起来看看。 我们首先看一下项目结构。 再看看构建时间。 为了方便大家看构建时间,我简单写了一个webpack,plugin ConsolePlugin ,记录了webpack在一次compilation所用的时间。 const chalk = require('chalk') /* console 颜色 */ var slog = require('single-line-log'); /* 单行打印 console */ class ConsolePlugin { constructor(options){ this.options = options } apply(compiler){ /** * Monitor file change 记录当前改动文件 */ compiler.hooks.watchRun.tap('ConsolePlugin', (watching) => { const changeFiles = watching.watchFileSystem.watcher.mtimes for(let file in changeFiles){ console.log(chalk.green('当前改动文件:'+ file)) } }) /** * before a new compilation is created. 开始 compilation 编译 。 */ compiler.hooks.compile.tap('ConsolePlugin',()=>{ this.beginCompile() }) /** * Executed when the compilation has completed. 一次 compilation 完成。 */ compiler.hooks.done.tap('ConsolePlugin',()=>{ this.timer && clearInterval( this.timer ) const endTime = new Date().getTime() const time = (endTime - this.starTime) / 1000 console.log( chalk.yellow(' 编译完成') ) console.log( chalk.yellow('编译用时:' + time + '秒' ) ) }) } beginCompile(){ const lineSlog = slog.stdout let text = '开始编译:' /* 记录开始时间 */ this.starTime = new Date().getTime() this.timer = setInterval(()=>{ text += '█' lineSlog( chalk.green(text)) },50) } } 构建时间如下: ...

December 17, 2024

第12章 性能优化算法

“过早优化是万恶之源,但在正确的时机做正确的优化,则是工程艺术的巅峰。” 12.1 问题引入 一个成熟的 AI Agent 系统面临着多维度的性能挑战。Claude Code 作为一个包含数十万行 TypeScript 代码的终端 AI 助手,需要同时解决以下问题: 启动速度:用户在终端输入 claude 后,如何在亚秒级时间内完成初始化?数百个模块的加载、配置的解析、API 客户端的构建——任何一项阻塞都意味着体验的劣化。 上下文窗口管理:模型的上下文窗口是有限的(通常为 200K tokens),而一个长时间的编程会话可能产生数百万 tokens 的对话内容。如何在不超过窗口限制的前提下最大化信息密度? 内存控制:终端 UI 需要维护大量的渲染状态。一个包含 1000 条消息的会话,如果不做虚拟化,仅 React fiber 和 Yoga 布局节点就可能消耗 250MB 内存。 渲染流畅性:流式输出时每个 token 都会触发渲染更新。如何避免因频繁重绘导致的 CPU 峰值和卡顿? 本章将深入剖析 Claude Code 针对这些问题的系统性优化策略,从 Token 计数的精妙权衡,到上下文压缩的三级体系,再到终端渲染的虚拟滚动算法。每一项优化背后都蕴含着对"精确性"与"速度"之间平衡的深刻思考。 12.2 启动优化策略 12.2.1 性能度量驱动的优化 Claude Code 遵循一个朴素但有效的原则:不能度量的东西无法优化。启动阶段的每一个关键检查点都被 startupProfiler 精确记录: // startupProfiler.ts — 启动性能度量框架 const PHASE_DEFINITIONS = { import_time: ['cli_entry', 'main_tsx_imports_loaded'], init_time: ['init_function_start', 'init_function_end'], settings_time: ['eagerLoadSettings_start', 'eagerLoadSettings_end'], total_time: ['cli_entry', 'main_after_run'], } export function profileCheckpoint(name: string): void { if (!SHOULD_PROFILE) return // 非采样用户零开销 const perf = getPerformance() perf.mark(name) if (DETAILED_PROFILING) { memorySnapshots.push(process.memoryUsage()) } } 该框架采用分层采样策略:100% 的内部用户和 0.5% 的外部用户被采样记录启动性能指标,而完整的内存快照报告仅在显式启用 CLAUDE_CODE_PROFILE_STARTUP=1 时生成。这种设计确保了生产环境中性能数据的持续可观测性,同时将度量本身的开销降至最低。 ...

June 9, 2026

耗电-治理

前面几篇分别讲了耗电的原理、检测、以及CPU/网络/定位/屏幕的具体优化手段。本文把这些内容整合起来,给出一套面向工程团队的 耗电治理体系:从研发规范、线上监控、问题排查到常态化运营。 耗电治理是 APM 资源子系的一部分,与稳定性、流畅性、启动指标共用同一套监控与告警基础设施。完整的 APM 建设方案见 APM 系列:数据采集、B端平台设计。 一、治理体系全景 一个成熟的iOS耗电治理体系通常由四个闭环组成: flowchart TB subgraph Dev["研发侧"] R1["能效规范"] R2["架构层能效约束"] R3["Code Review清单"] end subgraph Test["测试侧"] T1["XCTest能效基线"] T2["真机续航测试"] T3["极端场景矩阵"] end subgraph Online["线上侧"] O1["MetricKit接入"] O2["自建CPU/电量采样"] O3["场景化埋点"] O4["大盘 + 告警"] end subgraph Gov["运营侧"] G1["版本耗电报告"] G2["Top问题跟进"] G3["复盘与规范迭代"] end Dev --> Test --> Online --> Gov --> Dev 二、研发阶段的能效规范 模块准入清单 每个新增模块在接入主App前,必须回答以下问题: 维度 问题 CPU 是否有常驻Timer?是否有轮询?是否在不可见时暂停? 网络 请求频率?是否合并?是否支持后台取消? 定位 是否申请定位?精度多少?后台策略? 传感器 是否开启传感器?采样率?视图生命周期? 后台能力 是否申请Background Mode?如何收敛? 动画/渲染 是否有长驻动画?是否适配ProMotion? Low Power 低电量模式下的降级策略? 权限 涉及的用户权限?是否遵守最小权限原则? 架构层面的能效约束 一些通用能力可以在架构层统一管控,避免各业务重复踩坑: ...

May 7, 2026