android_os

第27章:实时性与性能优化

Android系统的流畅性直接影响用户体验,本章深入剖析Android的实时性保证机制和性能优化策略。我们将从Linux内核调度器的Android定制开始,探讨如何通过RT调度、Jank检测、内存管理和功耗优化等技术手段,打造一个响应迅速、运行流畅的移动操作系统。通过与iOS、鸿蒙等竞争系统的对比,理解Android在实时性和性能优化方面的独特设计。

1. RT调度器应用

1.1 Android调度器架构概述

Android基于Linux内核的调度器,但针对移动设备的特点进行了大量优化。理解这些优化对于开发高性能应用至关重要。

CFS (Completely Fair Scheduler) 基础

Android默认使用CFS调度器,它通过红黑树维护可运行进程队列,使用虚拟运行时间(vruntime)保证公平性。关键概念包括:

Android对CFS的主要修改:

CFS内部实现细节

CFS使用struct sched_entity表示可调度实体,通过struct cfs_rq管理运行队列:

struct sched_entity {
    struct load_weight load;       // 权重信息
    struct rb_node run_node;       // 红黑树节点
    u64 exec_start;               // 执行开始时间
    u64 sum_exec_runtime;         // 累计执行时间
    u64 vruntime;                 // 虚拟运行时间
    u64 prev_sum_exec_runtime;    // 上次更新时的累计时间
};

权重计算机制

Linux内核定义了40个权重等级,nice值每差1,CPU时间相差约10%。权重数组prio_to_weight[]预计算了从nice -20到19的权重值:

nice -20: weight = 88761
nice   0: weight = 1024 (NICE_0_LOAD)
nice  19: weight = 15

这种指数级的权重差异确保了高优先级进程能获得显著更多的CPU时间。

与iOS调度器对比

与鸿蒙调度器对比

RT调度类实现

RT调度类为实时任务提供确定性调度保证,Android中主要用于:

RT调度特点:

RT调度实现原理

RT调度类使用优先级数组管理可运行任务:

struct rt_prio_array {
    DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1);  // 优先级位图
    struct list_head queue[MAX_RT_PRIO];    // 每个优先级的任务队列
};

SCHED_FIFO vs SCHED_RR

RT带宽控制机制

Linux 2.6.25引入RT带宽控制,防止RT任务饿死其他任务:

# 默认配置:每1秒内RT任务最多运行0.95秒
/proc/sys/kernel/sched_rt_period_us = 1000000  # 1秒
/proc/sys/kernel/sched_rt_runtime_us = 950000  # 0.95秒

超过配额后,RT任务会被限流(throttled),直到下个周期。这保证了系统始终有5%的时间处理非RT任务。

Android RT优先级分配策略

音频相关:
- AudioFlinger::MixerThread: 96-98
- FastMixer: 98
- AudioTrack回调: 95

图形相关:
- SurfaceFlinger主线程: 1-2 (使用nice值)
- RenderThread: 不使用RT,使用nice -10到-4
- HWC回调线程: 90

输入相关:
- InputReader: 91
- InputDispatcher: 92

与iOS实时性保证对比

EAS (Energy Aware Scheduling) 集成

Android 5.0开始集成EAS,实现性能与功耗的平衡:

关键函数:

PELT算法详解

PELT (Per-Entity Load Tracking)是EAS的核心,追踪每个任务的历史负载:

// 负载衰减公式:每1024us衰减一次
load = load * y + new_load * (1 - y)
// 其中 y = 0.978 (衰减因子)

PELT追踪三个关键指标:

能效模型定义

设备树中定义每个CPU的能效数据:

cpu-cost {
    cluster0 {
        busy-cost-data = <
            /* freq    power */
            300000    5
            600000    9
            900000    16
            1200000   27
        >;
        idle-cost-data = <
            /* state   power */
            0          0    /* WFI */
            1          0    /* retention */
            2          0    /* power collapse */
        >;
    };
};

EAS决策过程

  1. 计算任务在每个CPU上的预期能耗
  2. 考虑任务迁移成本(cache miss等)
  3. 选择能耗最低的CPU
  4. 触发频率调整

Android特有的EAS扩展

与其他系统能效调度对比

与Linux主线调度器差异

Android调度器的主要差异:

  1. schedtune控制器:提供per-cgroup的性能提升
  2. prefer_idle:优先选择空闲CPU
  3. uclamp:细粒度的utilization clamping
  4. RTG (Related Thread Groups):相关线程组优化

1.2 实时线程管理

RT优先级设计

Android系统服务的RT优先级分配遵循严格规范:

优先级分配原则:
99: 仅用于关键中断处理
95-98: 音频HAL回调线程
90-94: 触摸事件处理
85-89: 显示相关线程
80-84: 相机服务
1-79: 其他RT需求

关键API:

音频线程实时性保证

音频是Android中对实时性要求最高的子系统:

FastMixer线程

音频策略

优化技巧:

渲染线程优先级调优

UI渲染流畅性直接影响用户体验:

RenderThread配置

HWUI渲染管线

  1. UI线程:构建DisplayList
  2. RenderThread:OpenGL命令生成
  3. GPU:异步渲染
  4. SurfaceFlinger:合成显示

优化要点:

触摸响应优化

触摸延迟是用户最敏感的性能指标:

InputDispatcher优化

延迟优化技术

  1. 硬件层:高采样率触摸屏(120Hz+)
  2. 驱动层:中断亲和性绑定
  3. 框架层:批处理与预测
  4. 应用层:异步触摸处理

关键指标:

1.3 CPU亲和性与大小核调度

big.LITTLE架构支持

Android深度优化了ARM big.LITTLE架构支持:

集群类型

调度域构建

MC domain: 同集群内CPU
DIE domain: 跨集群调度
NUMA domain: 多芯片系统

能效感知

任务迁移策略

任务在大小核间迁移的决策机制:

迁移触发条件

  1. 负载不均衡load_balance()周期性检查
  2. 唤醒时select_task_rq()选择目标CPU
  3. 能效优化:EAS主动迁移
  4. 温度限制:thermal governor介入

迁移成本考虑

HMP (Heterogeneous Multi-Processing) 策略

热插拔机制

CPU热插拔用于极限省电场景:

核心组件

Android定制

  1. msm_performance:高通平台优化
  2. core_ctl:智能核心控制
  3. isolation:CPU隔离机制

优化建议

DynamIQ集群管理

ARM DynamIQ带来更灵活的CPU配置:

关键特性

调度适配

性能优化

2. Jank检测与优化

2.1 Jank产生机制分析

Jank(卡顿)是指UI渲染无法跟上显示刷新率,导致的视觉不连续现象。理解Jank产生的根本原因是优化的第一步。

帧渲染管线

Android的图形渲染采用生产者-消费者模型,涉及多个阶段:

渲染管线阶段

  1. Input:触摸事件产生和分发
  2. Animation:动画计算和属性更新
  3. Measure/Layout:视图树测量和布局
  4. Draw:构建DisplayList
  5. Sync:同步RenderThread
  6. Render:GPU命令生成
  7. Swap:缓冲区交换
  8. Composite:SurfaceFlinger合成

关键时间节点

性能监控点

VSYNC机制

VSYNC是Android图形系统的心跳,协调整个渲染流程:

VSYNC分发机制

HW Composer → SurfaceFlinger → Choreographer → App

三种VSYNC

  1. HW VSYNC:硬件产生的真实信号
  2. SW VSYNC:软件模拟的VSYNC
  3. App VSYNC:应用接收的VSYNC,有phase offset

Phase offset优化

DispSync模型

Triple Buffering

Android使用多缓冲技术平衡性能和延迟:

缓冲区角色

  1. Front Buffer:正在显示的缓冲区
  2. Back Buffer:正在渲染的缓冲区
  3. Third Buffer:备用缓冲区,处理掉帧

BufferQueue机制

优化策略

掉帧原因分类

深入理解各种掉帧原因有助于针对性优化:

1. CPU限制

2. GPU限制

3. 系统资源竞争

4. 框架问题

2.2 Systrace与Perfetto工具链

性能分析工具是Jank优化的利器,Android提供了强大的工具链。

Atrace架构

Atrace是Android的系统级跟踪框架:

核心组件

跟踪类别

gfx: 图形系统
input: 输入系统
view: View系统
wm: 窗口管理器
am: 活动管理器
sm: 同步管理器
audio: 音频系统
video: 视频系统
camera: 相机系统

使用方式

Perfetto数据收集

Perfetto是新一代跟踪系统,提供更强大的功能:

架构优势

核心组件

  1. traced:系统守护进程
  2. traced_probes:数据源服务
  3. perfetto SDK:应用集成库
  4. trace processor:数据分析引擎

数据源类型

配置示例

TraceConfig {
  duration_ms: 10000
  buffers {
    size_kb: 65536
  }
  data_sources {
    config {
      name: "linux.ftrace"
      ftrace_config {
        ftrace_events: "sched/*"
        ftrace_events: "power/*"
      }
    }
  }
}

性能指标分析

关键性能指标帮助量化Jank程度:

帧时间指标

计算公式

Jank率 = Janky帧数 / 总帧数
帧时间预算 = 1000ms / 刷新率
超时帧 = 实际帧时间 > 帧时间预算

分析维度

  1. 时间分解:各阶段耗时
  2. 线程分析:CPU利用率和等待
  3. 系统负载:CPU/GPU/内存使用率
  4. 热点识别:最耗时的函数

自动化Jank检测

自动化检测帮助持续监控性能:

JankStats库

JankStats.createAndTrack(
    window,
    frameListener = { frameData ->
        if (frameData.isJank) {
            // 记录Jank信息
        }
    }
)

FrameMetricsAggregator

CI/CD集成

2.3 Choreographer优化

Choreographer是Android动画和UI更新的中枢,优化它对改善Jank至关重要。

帧调度机制

Choreographer负责协调所有UI更新:

回调类型

  1. INPUT:输入事件处理
  2. ANIMATION:动画更新
  3. INSETS_ANIMATION:窗口动画
  4. TRAVERSAL:布局和绘制
  5. COMMIT:提交帧

调度流程

1. scheduleVsyncLocked()  请求VSYNC
2. onVsync()  接收VSYNC信号  
3. doFrame()  执行帧回调
4. doCallbacks()  按类型执行回调

优化要点

Input latency优化

减少输入延迟提升交互体验:

延迟组成

  1. 硬件延迟:触摸屏扫描
  2. 驱动延迟:中断处理
  3. 系统延迟:事件分发
  4. 应用延迟:事件处理
  5. 渲染延迟:UI更新

优化技术

测量方法

window.addOnFrameMetricsAvailableListener(
    { window, frameMetrics, dropCount ->
        val inputDelay = frameMetrics.getMetric(
            FrameMetrics.INPUT_HANDLING_DURATION
        )
    }
)

动画性能调优

流畅动画是良好用户体验的关键:

动画类型优化

  1. 属性动画:使用RenderNodeAnimator
  2. 过渡动画:缓存场景状态
  3. 物理动画:使用SpringAnimation
  4. Lottie动画:硬件加速渲染

优化技巧

动画监控

animator.addUpdateListener { animation ->
    // 避免复杂计算
    view.translationX = animation.animatedValue as Float
}

RenderThread优化

RenderThread负责GPU命令生成:

线程特性

优化策略

  1. 减少绘制复杂度
    • 简化自定义View
    • 使用Canvas.quickReject()
    • 避免saveLayer()
  2. 纹理优化
    • 使用纹理图集
    • 及时释放纹理
    • 控制纹理大小
  3. 命令优化
    • 批量绘制调用
    • 状态变化最小化
    • 使用Display List

调试工具

3. 内存压力处理

3.1 低内存管理器演进

Android的低内存管理经历了从内核空间到用户空间的重要演进,这种变化带来了更灵活和智能的内存管理策略。

LMK到LMKD迁移

LMK (Low Memory Killer) 时代

传统的LMK是内核模块,存在以下问题:

LMKD (Low Memory Killer Daemon) 优势

LMKD作为用户空间守护进程,提供了:

架构对比

LMK: Kernel → 直接杀进程
LMKD: Kernel → PSI/vmpressure → LMKD → ActivityManager → 杀进程

LMKD工作流程

  1. 监听内存压力事件
  2. 计算当前内存状态
  3. 选择牺牲进程
  4. 通过socket通知kill
  5. 更新系统状态

PSI (Pressure Stall Information) 集成

PSI是Linux 4.20引入的压力监控机制,Android充分利用了这一特性:

PSI指标类型

内存压力监控

/proc/pressure/memory
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0

LMKD中的PSI使用

与vmpressure对比

内存水位线调整

Android定义了多级内存水位线来触发不同的回收行为:

水位线级别

Critical (致命) < Low (低) < Pressure (压力) < Medium (中等) < High (高)

计算方式

关键属性

自适应调整

minfree = property_get_int32("ro.lmk.low", 1024);
if (mem_total < 2048) {
    minfree = minfree * mem_total / 2048;
}

进程优先级oom_adj

oom_adj是决定进程生死的关键指标:

adj值范围与含义

-1000: 不可杀死(系统关键进程)
-900: 持久进程(persistent)
-800: 系统进程
-700: 持久服务
0: 前台应用
100: 可见应用
200: 可感知应用
300: 备份应用
400-500: 服务进程
600-700: Home应用
800-900: 缓存进程
900+: 空进程

计算因素

  1. 进程状态:前台/后台/服务
  2. 组件状态:Activity/Service/Provider
  3. 客户端重要性:绑定服务的客户端
  4. 用户交互:最近使用时间

优化策略

3.2 内存回收策略

内存回收是维持系统流畅的关键机制,Android在Linux基础上进行了大量优化。

kswapd工作机制

kswapd是内核的内存回收线程,负责异步页面回收:

触发条件

回收流程

  1. balance_pgdat(): 主循环
  2. shrink_zone(): 回收特定zone
  3. shrink_lruvec(): LRU链表回收
  4. shrink_slab(): slab缓存回收

Android优化

监控指标

cat /proc/vmstat | grep -E "pgsteal|pgscan|pgfree"

Direct reclaim优化

Direct reclaim是同步回收路径,直接影响应用性能:

问题分析

优化措施

  1. 减少触发
    • 预留更多内存
    • 提前异步回收
    • 使用内存池
  2. 缩短时间
    • 限制回收页面数
    • 优先回收易释放页面
    • 避免回收代码页
  3. 进程隔离
    • 关键进程使用mlockall()
    • 内存cgroup隔离
    • 专用内存预留

内核参数调优

echo 0 > /proc/sys/vm/direct_reclaim_anon
echo 200 > /proc/sys/vm/direct_reclaim_swappiness

ZRAM压缩

ZRAM提供内存压缩功能,有效扩展可用内存:

工作原理

配置优化

# 设置ZRAM大小
echo 2G > /sys/block/zram0/disksize
# 选择压缩算法
echo lz4 > /sys/block/zram0/comp_algorithm
# 启用ZRAM
mkswap /dev/block/zram0
swapon /dev/block/zram0

压缩算法对比

性能监控

cat /sys/block/zram0/mm_stat
# 输出:原始大小 压缩大小 内存使用 ...

ION内存池管理

ION是Android的统一内存分配器,管理各种内存池:

内存池类型

  1. SYSTEM: 普通内存
  2. SYSTEM_CONTIG: 连续内存
  3. CARVEOUT: 预留内存
  4. CMA: 连续内存分配器
  5. DMA: DMA缓冲区

分配策略

ion_alloc(len, heap_mask, flags)
 选择合适heap
 分配内存
 创建ion_buffer
 返回fd

内存共享机制

优化建议

3.3 应用内存优化

应用层的内存优化对系统整体性能至关重要。

内存泄漏检测

内存泄漏是导致应用性能下降和被杀的主要原因:

常见泄漏类型

  1. Context泄漏:静态引用Activity
  2. Handler泄漏:内部类持有外部引用
  3. 监听器泄漏:未注销的监听器
  4. 集合泄漏:静态集合持有对象
  5. 线程泄漏:未结束的线程

检测工具

LeakCanary原理

  1. 监控对象生命周期
  2. 主动触发GC
  3. 检查引用链
  4. 生成泄漏报告

防止泄漏的最佳实践

// 使用弱引用
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivity;
}

// 及时清理
@Override
protected void onDestroy() {
    handler.removeCallbacksAndMessages(null);
    unregisterReceiver(receiver);
}

Bitmap内存管理

Bitmap是Android应用最大的内存消耗源:

内存计算

内存大小 = 宽度 × 高度 × 每像素字节数
ARGB_8888: 4字节/像素
RGB_565: 2字节/像素

优化策略

  1. 合适的解码配置
    options.inSampleSize = 2; // 缩放
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    
  2. 复用Bitmap
    options.inBitmap = reusableBitmap;
    options.inMutable = true;
    
  3. 及时回收
    bitmap.recycle();
    bitmap = null;
    

高级优化

Native内存追踪

Native内存不受Java堆限制,需要特别关注:

追踪工具

malloc_debug使用

adb shell setprop libc.debug.malloc.program app_process
adb shell setprop libc.debug.malloc.options backtrace=16

常见问题

  1. 内存泄漏:未释放的malloc
  2. 越界访问:数组越界
  3. 重复释放:double free
  4. 野指针:use after free

JNI内存管理

// 创建全局引用
jobject globalRef = (*env)->NewGlobalRef(env, localRef);
// 及时删除
(*env)->DeleteGlobalRef(env, globalRef);

ART堆调优

ART运行时提供了多种堆调优参数:

堆大小设置

<application android:largeHeap="true">

运行时参数

GC调优

// 主动触发GC
System.gc();
Runtime.getRuntime().gc();

// 获取内存信息
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memInfo);

分代管理

监控指标

adb shell dumpsys meminfo <package>
# 查看详细内存使用情况

4. 功耗优化策略

4.1 CPU功耗管理

CPU是移动设备的主要功耗来源,Android通过多层次的功耗管理机制实现能效优化。

CPUFreq governor

CPUFreq子系统负责动态调整CPU频率,不同的governor实现不同的调频策略:

常用Governor

  1. schedutil (推荐):
    • 与调度器深度集成
    • 基于CPU利用率调频
    • 响应更快,更省电
  2. interactive
    • Android传统选择
    • 快速响应用户交互
    • 可配置的升降频参数
  3. ondemand
    • Linux默认governor
    • 基于负载的调频
    • 相对保守
  4. performance/powersave
    • 固定最高/最低频率
    • 用于测试和特殊场景

schedutil工作原理

freq = max_freq * util / max_capacity
// util: CPU利用率
// max_capacity: CPU最大能力

调优参数

监控和调试

# 查看当前governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# 查看可用频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies

# 实时频率监控
watch -n 1 'cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq'

CPU Idle states

CPU空闲时进入不同深度的休眠状态以节省功耗:

Idle状态级别

C0: Active (运行状态)
C1: WFI (Wait for Interrupt) - 最浅休眠
C2: 时钟关闭
C3: 电源门控
C4+: 深度休眠,可能关闭缓存

cpuidle框架

Android优化

  1. idle状态选择
    • 考虑唤醒延迟
    • 预测空闲时长
    • 平衡功耗和性能
  2. cluster idle
    • 整个CPU集群休眠
    • 更深的省电状态
    • 需要所有核心空闲

调优建议

# 查看idle状态
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name

# 禁用深度idle(调试用)
echo 1 > /sys/devices/system/cpu/cpu0/cpuidle/state3/disable

调频策略优化

根据不同场景优化调频策略:

场景识别

  1. 用户交互:快速升频
  2. 后台任务:保守调频
  3. 游戏模式:性能优先
  4. 省电模式:限制最高频率

Boost机制

// Input boost
on_touch_event() {
    cpu_boost(300ms, 1.4GHz);
}

// Launch boost  
on_app_launch() {
    cpu_boost(1000ms, max_freq);
}

频率表优化

与调度器协同

Turbo boost控制

现代CPU支持短时超频以提升突发性能:

Turbo机制

Android管理

  1. thermal限制
    # thermal配置
    /vendor/etc/thermal-engine.conf
    
  2. 功耗预算
    • 瞬时功耗限制
    • 平均功耗限制
    • 电池温度考虑
  3. 应用场景
    • 应用启动加速
    • 相机拍照处理
    • 游戏加载场景

监控指标

# CPU温度
cat /sys/class/thermal/thermal_zone*/temp

# 频率限制
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq

4.2 系统级功耗优化

Android提供了多种系统级的功耗优化机制。

Doze模式实现

Doze是Android 6.0引入的深度休眠机制:

Doze状态机

ACTIVE → INACTIVE → IDLE_PENDING → SENSING → LOCATING → IDLE → IDLE_MAINTENANCE

进入条件

  1. 屏幕关闭
  2. 电池供电
  3. 静止不动
  4. 未充电

Doze限制

Light Doze

白名单机制

<!-- 系统白名单 -->
/system/etc/sysconfig/

<!-- 应用请求 -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>

调试方法

# 强制进入Doze
adb shell dumpsys deviceidle force-idle

# 退出Doze
adb shell dumpsys deviceidle unforce

# 查看状态
adb shell dumpsys deviceidle

App Standby机制

App Standby限制不常用应用的后台活动:

Standby分组 (Android P+):

  1. Active: 当前使用
  2. Working Set: 经常使用
  3. Frequent: 定期使用
  4. Rare: 很少使用
  5. Restricted: 限制最严

分组依据

限制内容

豁免条件

API适配

// 查询standby bucket
UsageStatsManager usm = getSystemService(UsageStatsManager.class);
int bucket = usm.getAppStandbyBucket();

// 监听变化
registerReceiver(new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        // Standby状态改变
    }
}, new IntentFilter(UsageStatsManager.ACTION_STANDBY_BUCKET_CHANGED));

JobScheduler优化

JobScheduler提供了智能的后台任务调度:

优化策略

  1. 批处理执行
    • 合并相似任务
    • 统一唤醒时机
    • 减少唤醒次数
  2. 条件约束
    JobInfo.Builder builder = new JobInfo.Builder(jobId, serviceComponent)
        .setRequiredNetworkType(NetworkType.UNMETERED)
        .setRequiresCharging(true)
        .setRequiresDeviceIdle(true)
        .setPersisted(true);
    
  3. 延迟容忍
    • setMinimumLatency(): 最小延迟
    • setOverrideDeadline(): 最大延迟
    • 让系统选择最优时机

与Doze协同

最佳实践

WakeLock管理

WakeLock防止设备休眠,需要谨慎使用:

WakeLock类型

  1. PARTIAL_WAKE_LOCK
    • CPU保持运行
    • 屏幕和键盘灯可关闭
  2. SCREEN_DIM_WAKE_LOCK
    • CPU、屏幕开启
    • 屏幕变暗
  3. SCREEN_BRIGHT_WAKE_LOCK
    • CPU、屏幕全亮
  4. FULL_WAKE_LOCK
    • CPU、屏幕、键盘全开

使用原则

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp:MyWakelockTag");
wl.acquire(timeout); // 总是设置超时
try {
    // 执行工作
} finally {
    wl.release(); // 确保释放
}

系统优化

调试工具

# 查看WakeLock统计
adb shell dumpsys power | grep -i wake

# 查看持有者
adb shell dumpsys power | grep "Wake Locks"

4.3 硬件协同优化

功耗优化需要软硬件协同工作。

GPU DVFS

GPU动态电压频率调节(DVFS)类似CPU调频:

GPU Governor

  1. simple_ondemand
    • 基于GPU负载
    • 简单有效
  2. msm-adreno-tz
    • 高通方案
    • Trustzone辅助
  3. mali_dvfs
    • ARM Mali方案
    • 多级频率

调优参数

# GPU频率查看
cat /sys/class/kgsl/kgsl-3d0/devfreq/cur_freq

# 可用频率
cat /sys/class/kgsl/kgsl-3d0/devfreq/available_frequencies

# GPU负载
cat /sys/class/kgsl/kgsl-3d0/gpu_busy_percentage

优化策略

Display功耗优化

显示屏是最大的功耗组件之一:

硬件特性

  1. OLED优化
    • 黑色像素不耗电
    • 支持Dark mode
    • AOD (Always On Display)
  2. 刷新率调节
    • 自适应刷新率
    • 静态内容降频
    • 视频匹配帧率
  3. 亮度管理
    • 自动亮度算法
    • 环境光感应
    • 内容自适应

软件优化

LTPO技术

// 动态刷新率
Display.Mode[] modes = display.getSupportedModes();
displayManager.setDesiredDisplayModeSpecs(
    displayId, 
    new Display.Mode[]{targetMode}
);

5G modem省电

5G带来更大的功耗挑战:

省电技术

  1. EN-DC优化
    • 4G/5G智能切换
    • 仅数据业务用5G
  2. 天线功耗
    • 动态天线调谐
    • MIMO配置优化
  3. DRX (非连续接收)
    • 周期性休眠
    • 快速唤醒

软件策略

AI加速器功耗控制

NPU/DSP等AI加速器的功耗管理:

功耗特点

优化方法

  1. 任务调度
    • 批处理推理
    • 负载均衡
    • 优先级管理
  2. 精度优化
    • INT8量化
    • 模型压缩
    • 动态精度
  3. 硬件控制
    • 动态电压调节
    • 核心开关
    • 内存带宽控制

框架集成

// NNAPI功耗提示
ANeuralNetworksCompilation_setPreference(
    compilation,
    ANEURALNETWORKS_PREFER_LOW_POWER
);

监控工具

本章小结

本章深入探讨了Android系统的实时性保证和性能优化策略。我们从Linux内核调度器的Android定制开始,了解了RT调度、EAS能效调度、大小核架构等关键技术。在Jank检测与优化部分,我们分析了Android图形渲染管线、VSYNC机制、Choreographer调度,以及使用Systrace/Perfetto进行性能分析的方法。内存压力处理章节介绍了从LMK到LMKD的演进、PSI压力监控、内存回收策略以及应用层内存优化技巧。最后,我们探讨了从CPU、系统到硬件层面的全方位功耗优化策略。

关键要点:

与其他系统对比:

练习题

基础题

  1. RT调度理解 解释Android中RT调度类与CFS调度类的主要区别,并列举至少3个使用RT调度的系统组件。

    提示 考虑调度策略、优先级范围、抢占规则等方面的差异。
    参考答案 主要区别: - 调度策略:RT使用SCHED_FIFO/SCHED_RR,CFS使用SCHED_NORMAL - 优先级:RT优先级1-99,数值越大优先级越高;CFS使用nice值-20到19 - 抢占规则:RT任务总是抢占CFS任务,高优先级RT抢占低优先级 - 时间片:RT可以无限运行(受限于rt_runtime),CFS基于虚拟运行时间公平分配 使用RT调度的组件: - AudioFlinger (音频混音线程) - InputDispatcher (触摸事件分发) - SurfaceFlinger (关键渲染路径) - Camera HAL (预览线程) - FastMixer (低延迟音频)
  2. Jank检测方法 描述如何使用dumpsys gfxinfo命令分析应用的渲染性能,解释输出中的关键指标。

    提示 关注帧时间统计、各阶段耗时、janky帧比例等。
    参考答案 使用方法: ```bash adb shell dumpsys gfxinfo ``` 关键指标: - Total frames rendered: 总渲染帧数 - Janky frames: 超过阈值的帧数和百分比 - 50th/90th/95th/99th percentile: 帧时间分布 - Frame time各阶段:Input、Animation、Measure、Layout、Draw、Sync、Command、Swap - Number Missed Vsync: 错过的VSYNC数 - Number High input latency: 高输入延迟次数 通过这些指标可以识别性能瓶颈在哪个阶段。 </details>
  3. 内存压力信号 比较vmpressure和PSI两种内存压力监控机制的优缺点。

    提示 考虑准确性、开销、信息粒度等方面。
    参考答案 vmpressure: - 优点:实现简单,开销小,长期稳定 - 缺点:粒度粗,只有low/medium/critical三级,可能误报 PSI (Pressure Stall Information): - 优点:更准确反映实际压力,区分some/full压力,提供时间窗口平均值 - 缺点:需要Linux 4.20+,计算开销稍大 PSI通过追踪任务因资源等待而阻塞的时间,提供了更准确的压力指标,Android Q开始优先使用PSI。
  4. 功耗优化基础 列举Doze模式的进入条件和主要限制,说明Light Doze与Deep Doze的区别。

    提示 考虑设备状态、网络访问、唤醒锁等方面。
    参考答案 Doze进入条件: - 屏幕关闭 - 电池供电(未充电) - 设备静止(加速度计检测) - 一段时间无用户交互 主要限制: - 网络访问暂停(除高优先级FCM) - WakeLock被忽略 - 标准Alarm延迟(除setAlarmClock) - WiFi扫描停止 - 同步和作业延迟执行 Light Doze vs Deep Doze: - Light:设备在口袋中移动时触发,保持网络连接,限制较宽松 - Deep:设备完全静止时触发,断开网络,进入深度省电

挑战题

  1. 调度器优化方案 设计一个优化方案,解决游戏应用在复杂场景下的卡顿问题。需要考虑CPU调度、GPU渲染、内存管理等多个方面。

    提示 从游戏线程优先级、CPU亲和性、内存预分配、thermal管理等角度思考。
    参考答案 综合优化方案: 1. CPU调度优化: - 游戏主线程设置为SCHED_FIFO,RT优先级80-85 - 渲染线程绑定到大核,使用CPU亲和性 - 启用Game Mode,触发CPU boost - 禁用小核或限制后台任务到小核 2. 内存管理: - 预分配内存池,避免运行时分配 - 调高oom_adj分数,减少被杀概率 - 使用mlockall()锁定关键内存页 - 提前加载资源,避免运行时IO 3. GPU优化: - 根据场景复杂度动态调整渲染分辨率 - 使用VRS (Variable Rate Shading) - 优化Draw Call批处理 - 监控GPU温度,动态调整画质 4. 系统协同: - 请求性能模式,禁用省电特性 - 使用Sustained Performance Mode - 监控thermal状态,提前降低负载 - 与厂商游戏加速框架集成
  2. 内存泄漏诊断 一个社交应用在后台运行数小时后被系统频繁杀死,设计完整的诊断和优化流程。

    提示 考虑内存泄漏检测、后台任务优化、内存占用分析等。
    参考答案 诊断流程: 1. 初步分析: ```bash # 查看内存信息 adb shell dumpsys meminfo # 查看oom_adj adb shell cat /proc//oom_adj # 查看内存压力 adb shell cat /proc/pressure/memory ``` 2. 泄漏检测: - 集成LeakCanary监控泄漏 - 使用Android Studio Profiler录制内存 - 分析heap dump找到泄漏对象 - 检查静态引用、Handler、监听器等 3. 后台优化: - 将Service改为JobIntentService - 使用WorkManager替代AlarmManager - 及时释放不需要的资源 - 实现onTrimMemory()响应内存压力 4. 内存优化: - 优化图片缓存大小和策略 - 使用更高效的数据结构 - 减少内存中的数据冗余 - Native内存及时释放 5. 监控方案: - 建立内存使用基准线 - 监控关键场景内存增长 - 设置内存告警阈值 - 定期进行内存回归测试 </details>
  3. 跨平台性能对比 分析Android、iOS和鸿蒙在处理高帧率(120Hz)游戏时的架构差异和优化策略。

    提示 从渲染管线、调度策略、功耗管理等角度对比。
    参考答案 架构对比: Android: - Choreographer基于VSYNC的帧调度 - Triple Buffering减少掉帧 - RenderThread独立渲染 - 支持Variable Refresh Rate - Vulkan/OpenGL ES渲染 iOS: - CADisplayLink驱动的渲染循环 - Metal渲染,更低开销 - ProMotion自适应刷新率 - 统一内存架构,减少拷贝 - 更严格的后台限制 鸿蒙: - 图形图像子系统支持分布式渲染 - ArkUI声明式UI框架 - 基于Vulkan的统一渲染 - 智能调度考虑多设备协同 - 方舟编译器优化 优化策略差异: - Android:开放但碎片化,需要适配更多硬件 - iOS:封闭优化,硬件软件深度集成 - 鸿蒙:分布式场景优化,跨设备体验一致性
  4. 实时音频处理优化 设计一个专业音频应用的低延迟处理方案,要求往返延迟小于10ms。

    提示 考虑音频HAL、缓冲区大小、线程优先级、CPU亲和性等。
    参考答案 低延迟方案设计: 1. 音频路径优化: - 使用AAudio的EXCLUSIVE模式 - 设置PERFORMANCE模式 - 最小缓冲区配置(2-3个period) - 采样率匹配硬件原生率 2. 线程配置: ```c // 音频回调线程 pthread_setschedparam(thread, SCHED_FIFO, 95); // CPU亲和性 cpu_set_t cpuset; CPU_SET(6, &cpuset); // 绑定到大核 pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset); ``` 3. 内存优化: - 预分配所有音频缓冲区 - 使用lock-free环形缓冲区 - mlockall()锁定内存 - 避免动态内存分配 4. 系统配置: - 关闭CPU频率调节 - 禁用C-states深度休眠 - 提高中断亲和性优先级 - 使用RT throttling保护 5. 延迟测量: - 硬件loopback测试 - 时间戳精确测量 - 分析各阶段延迟 - 持续监控性能指标

常见陷阱与错误

调度器相关

  1. RT优先级滥用
    • 错误:给所有线程设置RT优先级
    • 后果:系统响应变差,可能导致看门狗超时
    • 正确:仅关键实时任务使用RT,合理分配优先级
  2. CPU亲和性误用
    • 错误:将所有线程绑定到大核
    • 后果:大核过载,小核空闲,功耗增加
    • 正确:根据任务特性分配,考虑热平衡

性能分析错误

  1. Systrace数据误读
    • 错误:只看单帧数据下结论
    • 问题:偶发问题被忽略
    • 正确:收集足够样本,分析分布情况
  2. 过度优化
    • 错误:为了性能牺牲所有功能
    • 问题:用户体验下降
    • 正确:平衡性能和功能,数据驱动优化

内存管理陷阱

  1. 内存泄漏忽视
    • 错误:认为GC会处理一切
    • 后果:应用被频繁杀死
    • 正确:主动检测和修复泄漏
  2. Native内存失控
    • 错误:只关注Java堆
    • 问题:Native内存超限导致OOM
    • 正确:全面监控Java堆和Native内存

功耗优化误区

  1. WakeLock泄漏
    • 错误:acquire后忘记release
    • 后果:电池快速耗尽
    • 正确:使用try-finally确保释放
  2. 忽视后台限制
    • 错误:假设后台可以自由运行
    • 问题:被系统限制或杀死
    • 正确:适配Doze和App Standby

最佳实践检查清单

设计阶段

开发阶段

测试阶段

优化阶段

维护阶段