第 19 篇 驱动性能优化与功耗优化实战

目录

开篇先划重点:优化的核心原则

第一部分:驱动性能优化实战

一、驱动代码级优化,最直接、最高效的优化

[1. 中断处理优化,降低中断占用](#1. 中断处理优化,降低中断占用)

[2. 数据拷贝优化,减少内存开销](#2. 数据拷贝优化,减少内存开销)

[3. 锁机制优化,减少死锁和 CPU 自旋](#3. 锁机制优化,减少死锁和 CPU 自旋)

[4. 轮询改事件驱动,彻底杜绝 CPU 空转](#4. 轮询改事件驱动,彻底杜绝 CPU 空转)

二、内核机制优化,提升系统整体性能

[1. 内核抢占与实时性优化](#1. 内核抢占与实时性优化)

[2. DMA 传输优化,解放 CPU](#2. DMA 传输优化,解放 CPU)

[3. 缓存优化,提升内存访问效率](#3. 缓存优化,提升内存访问效率)

[三、RK 平台硬件特性利用,最大化性能](#三、RK 平台硬件特性利用,最大化性能)

第二部分:驱动功耗优化实战

一、驱动级功耗优化,最核心的优化环节

[1. 时钟门控:不用的时钟,全部关掉](#1. 时钟门控:不用的时钟,全部关掉)

[2. 电源域管理:空闲的模块,彻底断电](#2. 电源域管理:空闲的模块,彻底断电)

[3. GPIO 功耗优化,杜绝浮空引脚](#3. GPIO 功耗优化,杜绝浮空引脚)

[4. 中断与唤醒优化,降低系统唤醒次数](#4. 中断与唤醒优化,降低系统唤醒次数)

二、系统级功耗优化

三、硬件级功耗优化

三、优化效果验证与测试方法

[1. 性能测试工具](#1. 性能测试工具)

[2. 功耗测试工具](#2. 功耗测试工具)

结尾说两句


大家好,我是黒漂技术佬。上一篇我们完成了智能门禁的全栈项目实战,很多兄弟后台问:

"佬,我的驱动功能实现了,但是运行起来 CPU 占用很高,还有工业场景里,设备是电池供电的,功耗特别大,续航很短,该怎么优化?"

这个问题非常核心,也是区分新手和资深驱动工程师的关键:功能实现只是入门,性能和功耗优化才是进阶,也是工业级量产产品的核心要求。很多新手写驱动,只关注功能能不能跑通,完全不考虑性能和功耗,结果到了量产阶段,出现 CPU 占用过高、系统卡顿、发热严重、续航太短的问题,根本无法落地。

今天这篇,我就把 RK3568 平台驱动开发的性能优化和功耗优化方法,全部分享给你,从底层驱动代码优化、内核配置优化,到系统级功耗管控,全流程实战,所有方法都可以直接落地到你的项目里。


开篇先划重点:优化的核心原则

在讲具体的优化方法之前,先给大家讲 3 条优化的核心铁律,避免你走弯路:

  1. 先测后优,不瞎优化:优化的第一步,永远是性能和功耗测试,找到瓶颈点,针对性优化,而不是上来就瞎改代码。没有测试数据支撑的优化,都是瞎折腾;
  2. 功能优先,优化为辅:必须先保证驱动功能完全正常、稳定,再做优化。绝对不能为了优化,牺牲功能的稳定性和正确性,工业级产品,稳定永远是第一位的;
  3. 二八原则,抓主要矛盾:80% 的性能损耗和功耗,都是由 20% 的核心代码和模块导致的,重点优化这 20% 的瓶颈点,就能获得 80% 的优化效果,不要在细枝末节上浪费时间。

第一部分:驱动性能优化实战

性能优化的核心目标,是降低驱动的 CPU 占用率、减少中断延迟、提高数据吞吐量、降低系统卡顿,我们从驱动代码优化、内核机制优化、硬件特性利用三个维度,讲最实用的优化方法。

一、驱动代码级优化,最直接、最高效的优化

代码是性能的基础,新手写的驱动,很多性能问题都是代码写的不规范导致的,这部分优化零成本,效果最明显。

1. 中断处理优化,降低中断占用

中断处理是驱动性能的核心瓶颈,尤其是高频中断(比如摄像头、音频、SPI 高速数据传输),中断处理函数执行时间太长,会导致系统实时性下降、CPU 占用升高、甚至丢失中断。

优化方法:

  • 严格遵守上半部 + 下半部机制:中断上半部只做最紧急、最耗时最短的工作,比如清除中断标志、记录硬件状态,所有耗时操作,全部放到下半部处理,绝对不能在中断上半部做耗时操作、循环、延时;
  • 选择合适的下半部机制 :
    • 高频、小数据量、不睡眠的操作,用tasklet,开销比工作队列小;
    • 耗时、需要睡眠的操作,用工作队列 workqueue;
    • 超高吞吐率的大数据传输(比如摄像头、网卡),用threaded_irq 线程化中断,把中断处理放到内核线程里执行,不阻塞硬中断,提高系统实时性;
  • 中断合并与节流:对于高频中断(比如传感器 1ms 一次的中断),可以做中断合并,累计一定数量的数据后,再一次处理,减少中断触发的次数;
  • 禁用不必要的中断:不需要的时候,及时关闭中断,比如设备空闲的时候,禁用中断,减少 CPU 的中断开销。

反面案例 vs 优化案例:

c

运行

复制代码
// 反面案例:中断上半部里做耗时操作,延时、循环,严重占用CPU
static irqreturn_t bad_irq_handler(int irq, void *dev_id)
{
    int i;
    // 耗时的数据处理,放在中断上半部,完全错误
    for (i = 0; i < 1024; i++) {
        data_buf[i] = read_reg(dev, i);
    }
    msleep(10); // 中断里睡眠,直接导致内核崩溃
    process_data(data_buf); // 耗时的算法处理
    return IRQ_HANDLED;
}

// 优化案例:严格的上半部+下半部,上半部只做最紧急的事
static irqreturn_t good_irq_handler(int irq, void *dev_id)
{
    // 1. 清除中断标志
    clear_irq_flag(dev);
    // 2. 调度下半部,耗时工作交给工作队列
    queue_work(dev->wq, &dev->work);
    // 3. 立刻返回,执行时间极短
    return IRQ_HANDLED;
}

// 下半部:工作队列处理函数,耗时操作都在这里
static void data_work_handler(struct work_struct *work)
{
    int i;
    struct my_dev *dev = container_of(work, struct my_dev, work);
    // 耗时的数据读取和处理,放在下半部
    for (i = 0; i < 1024; i++) {
        dev->data_buf[i] = read_reg(dev, i);
    }
    process_data(dev->data_buf);
}
2. 数据拷贝优化,减少内存开销

用户空间和内核空间的数据拷贝,是驱动里非常常见的性能损耗点,尤其是大数据量传输(比如摄像头、显示、音频),频繁的 copy_from_user/copy_to_user,会导致 CPU 占用急剧升高。

优化方法:

  • 减少拷贝次数:尽量一次拷贝完整的数据块,不要循环多次拷贝单个字节,比如要拷贝 1024 字节,一次拷贝完成,不要循环 1024 次,每次拷贝 1 字节;
  • 使用 mmap 内存映射:大数据量传输的场景(比如摄像头、视频采集),用 mmap 实现内核空间和用户空间的内存共享,零拷贝传输,完全避免 copy_from_user/copy_to_user 的开销。这是最高效的方式,摄像头、显示驱动都是用的 mmap;
  • 使用正确的内存分配函数 :
    • 连续物理内存,用dma_alloc_coherent(),适合 DMA 传输,避免 cache 不一致的问题;
    • 非连续内存,用kmalloc(),小内存分配,速度快,最大不超过 4KB;
    • 大内存分配,用vmalloc(),但是会有 TLB 开销,尽量少用;
  • 避免内存频繁申请释放:驱动初始化的时候,一次性申请好需要的内存缓冲区,驱动运行过程中循环复用,不要频繁的 kmalloc/kfree,减少内存碎片和分配开销。
3. 锁机制优化,减少死锁和 CPU 自旋

锁是用来保护共享资源的,但是用不好,会导致 CPU 自旋等待、死锁、系统卡顿,是新手最容易踩坑的地方。

优化方法:

  • 选择正确的锁类型 :
    • 中断上下文和进程上下文都要访问的共享资源,用自旋锁 spinlock,但是自旋锁持有时间必须极短,不能睡眠;
    • 只有进程上下文访问的共享资源,用互斥锁 mutex,开销比自旋锁小,支持睡眠,适合持有时间较长的场景;
    • 多读少写的场景,用读写锁 rwlock/seqlock,允许多个读者同时访问,提高并发性能;
  • 缩小锁的持有范围:只在访问共享资源的临界区持有锁,访问完成立刻释放,不要把无关的代码放到锁里,减少锁的持有时间;
  • 避免嵌套锁:绝对不要出现锁的嵌套,比如持有锁 A 的时候,申请锁 B,持有锁 B 的时候申请锁 A,必然导致死锁;
  • 禁用抢占和中断的时机 :自旋锁会自动禁用抢占,但是在中断上下文使用自旋锁的时候,必须用spin_lock_irqsave(),禁用本地中断,避免死锁。
4. 轮询改事件驱动,彻底杜绝 CPU 空转

很多新手写驱动,喜欢用轮询的方式读取硬件状态,比如开一个内核线程,每隔 1ms 读一次 GPIO 电平、读一次传感器数据,这会导致 CPU 一直被占用,即使没有数据,CPU 也在空转,性能损耗极大。

优化方法:

  • 所有的硬件事件,全部改成中断驱动,只有硬件触发中断的时候,才去处理数据,没有事件的时候,CPU 可以进入休眠,完全不占用资源;
  • 用户空间的事件等待,用poll/epoll + 等待队列 wait_queue实现,没有事件的时候,用户空间线程阻塞休眠,不占用 CPU,有事件的时候,内核唤醒线程处理,彻底杜绝轮询空转。

二、内核机制优化,提升系统整体性能

代码优化完成后,我们可以通过内核配置和机制优化,进一步提升驱动的性能,适配 RK3568 平台的硬件特性。

1. 内核抢占与实时性优化

RK3568 的安卓 SDK 默认的内核配置,是面向消费级产品的,对于工业级高实时性场景(比如运动控制、实时数据采集),可以通过内核配置优化,提升系统的实时性,降低中断和调度延迟:

  • 开启PREEMPT_RT实时补丁:工业级强实时场景,开启 RT 补丁,把 Linux 内核变成硬实时系统,中断延迟可以降到 100us 以内;
  • 开启PREEMPT_VOLUNTARY:自愿内核抢占,适合大多数场景,在不牺牲吞吐量的前提下,降低调度延迟;
  • 关闭DEBUG 相关的配置:内核里的 DEBUG 功能,比如 DEBUG_FS、LOCKDEP、KGDB,会带来很大的性能开销,量产版本必须关闭所有 DEBUG 配置。
2. DMA 传输优化,解放 CPU

对于大数据量的传输(比如音频、视频、SPI 高速数据),用 CPU 轮询拷贝数据,会占用大量的 CPU 资源,而 **DMA(直接内存访问)** 可以让外设直接和内存传输数据,不需要 CPU 干预,传输完成后,触发中断通知 CPU,完全解放 CPU。

RK 平台优化要点:

  • RK3568 内置了多个 DMA 控制器,所有的高速外设(I2S、SPI、I2C、CSI)都支持 DMA 传输,驱动里优先使用 DMA 传输,而不是 CPU 轮询的 PIO 模式;
  • 配置 DMA 的突发传输长度,匹配外设的 FIFO 大小,提高 DMA 传输效率,减少中断次数;
  • 使用 DMA 循环缓冲区,适合音频、视频这类连续的数据流传输,减少 DMA 的启动停止开销。
3. 缓存优化,提升内存访问效率

CPU 访问缓存的速度,比访问内存快几十倍,做好缓存优化,能极大提升驱动代码的执行效率。

优化方法:

  • 代码和数据对齐:频繁访问的结构体、缓冲区,按 CPU 缓存行对齐(RK3568 的 Cortex-A55 内核,缓存行是 64 字节),避免缓存行伪共享;
  • 连续物理内存:DMA 传输的缓冲区,用dma_alloc_coherent()分配连续的物理内存,避免 cache 和内存的数据不一致,减少 cache 刷新的开销;
  • 减少 cache 刷新:只有在必要的时候,才调用dma_sync_single_for_cpu/device()刷新 cache,不要频繁刷新,带来额外的开销。

三、RK 平台硬件特性利用,最大化性能

RK3568 有很多硬件加速特性,新手往往忽略了这些,用 CPU 去做大量的运算,导致性能很差,我们要充分利用硬件特性,把 CPU 解放出来。

  1. NPU 硬件加速:人脸识别、图像算法、AI 推理,全部放到 NPU 上执行,不要用 CPU 做运算,RK3568 的 1TOPS 算力的 NPU,做 AI 推理的速度,比 CPU 快几十倍;
  2. VPU 硬件编解码:视频的编码解码,用 RK 的硬件 VPU,通过 MPP 框架调用,不要用 CPU 做软编解码,CPU 占用率能从 90% 降到 10% 以内;
  3. RGA2D 硬件加速:图像的缩放、裁剪、格式转换,用 RK 的 RGA2D 硬件加速器,不要用 CPU 做像素运算,速度提升几十倍;
  4. CPU 核心调度优化:把高优先级的驱动线程、中断,绑定到大核上执行,RK3568 是 4 核 Cortex-A55,把实时性要求高的任务,绑定到固定的核心上,避免调度带来的延迟。

第二部分:驱动功耗优化实战

功耗优化,对于电池供电的便携设备、工业物联网设备,是生死攸关的指标,同样的硬件,优化好的设备,续航能提升几倍。功耗优化的核心目标,是在保证功能和性能的前提下,尽可能降低设备的整体功耗,我们从驱动级、系统级、硬件级三个维度,讲最实用的优化方法。

一、驱动级功耗优化,最核心的优化环节

设备的功耗,80% 都是由外设和驱动决定的,驱动级优化是功耗优化的核心,效果最明显。

1. 时钟门控:不用的时钟,全部关掉

RK3568 的每一个外设控制器,都有对应的时钟,时钟在运行的时候,即使外设不工作,也会消耗功耗。时钟门控,就是在外设空闲的时候,关闭它的时钟,需要工作的时候再打开,这是最基础、最高效的功耗优化方法。

优化方法:

  • 驱动里使用内核的Runtime PM 运行时电源管理框架,实现外设的时钟自动门控:外设打开的时候,使能时钟;外设关闭 / 空闲的时候,自动关闭时钟;
  • 不用的外设控制器,在设备树里直接设置status = "disabled",内核会直接关闭它的时钟,比如不用 SPI3、I2C4,直接在设备树里禁用;
  • 严格控制时钟频率,不需要高频工作的外设,尽量用低频率,比如 I2C 用 100kHz 就够了,不要用 400kHz,PWM 用 1kHz 就够了,不要用 10kHz。
2. 电源域管理:空闲的模块,彻底断电

比时钟门控更极致的优化,是电源域管理,RK3568 把不同的外设模块,分到了不同的电源域,外设完全空闲的时候,可以把整个电源域关掉,功耗几乎降到 0。

优化方法:

  • 驱动里实现系统的suspend/resume 休眠唤醒机制,系统进入休眠的时候,关闭外设的电源,系统唤醒的时候,再重新初始化外设;
  • 对于不常用的外设,比如 RFID、刷卡模块,只有在需要刷卡的时候,才给模块上电,用完立刻断电,不要一直上电;
  • 不用的硬件模块,直接切断供电,比如不用摄像头,就把摄像头的供电 regulator 关掉,不要只关时钟。
3. GPIO 功耗优化,杜绝浮空引脚

很多新手忽略了 GPIO 的功耗,大量浮空的 GPIO 引脚,会导致漏电,增加功耗,尤其是工业场景,引脚很多,累计的功耗非常可观。

优化方法:

  • 所有不用的 GPIO 引脚,在设备树里配置为下拉输入模式,避免浮空,减少漏电;
  • 输出模式的 GPIO,空闲的时候,设置为固定电平,不要来回跳变,减少功耗;
  • 输入模式的 GPIO,不需要中断的时候,关闭中断,只在需要的时候开启。
4. 中断与唤醒优化,降低系统唤醒次数

系统频繁的从休眠中唤醒,是功耗高的核心原因之一,每次唤醒,CPU 和各个模块都要上电,消耗大量的功耗。

优化方法:

  • 减少不必要的中断触发,比如传感器的采样频率,不需要 1ms 一次,改成 100ms 一次,中断次数减少 100 倍;
  • 合并中断事件,减少唤醒次数,比如多个传感器的数据,可以合并成一次中断唤醒,处理所有数据;
  • 严格控制唤醒源,只有必要的按键、刷卡、网络事件,才能唤醒系统,其他的中断,不能唤醒系统,在设备树里去掉wakeup-source属性。

二、系统级功耗优化

驱动优化完成后,我们可以通过安卓系统的配置,进一步降低整体功耗。

  1. CPU 调频优化 :使用ondemand 调频策略,系统负载低的时候,CPU 自动降到最低频率,负载高的时候,自动升到高频,平衡性能和功耗;电池供电的设备,用powersave策略,优先保证低功耗;
  2. 系统休眠优化:关闭安卓系统里不必要的后台服务、自启应用,减少系统唤醒次数,缩短系统亮屏时间,空闲的时候,自动进入深度休眠;
  3. 网络功耗优化:WiFi 不用的时候,自动关闭,有线网不用的时候,进入低功耗模式,减少网络模块的功耗;
  4. 关闭不必要的外设:不用蓝牙、WiFi、HDMI、USB 的时候,在系统里关闭对应的服务和电源,降低功耗。

三、硬件级功耗优化

功耗优化是软硬件结合的,硬件设计的好坏,直接决定了功耗的下限,这里给大家讲几个硬件设计的功耗优化要点,做产品的时候一定要注意:

  1. 电源方案优化:选择高效率的 DCDC 电源芯片,效率要在 90% 以上,轻载效率高的芯片,适合低功耗场景;LDO 只用于对纹波要求高的场景,因为 LDO 的效率低,功耗大;
  2. 外设供电设计:每个外设模块,都用单独的 MOS 管控制供电,不用的时候,MCU 可以直接切断模块的供电,而不是只靠软件关时钟;
  3. 电平匹配:所有外设都用 3.3V 电平,不要用 5V 电平,降低供电电压,减少功耗;
  4. 硬件防抖:按键、传感器的硬件防抖电路,减少软件的中断触发,降低系统唤醒次数。

三、优化效果验证与测试方法

前面讲过,先测后优,我们必须用专业的工具,测试优化前后的性能和功耗数据,验证优化效果。

1. 性能测试工具

表格

工具 作用 用法
top/htop 查看 CPU 占用率,看驱动的内核线程 / 中断的 CPU 占用 top -d 1,实时查看 CPU 占用
vmstat 查看系统的上下文切换、中断次数、内存使用 vmstat 1,每秒刷新一次
perf 内核性能分析工具,定位 CPU 占用高的函数 perf top,实时查看 CPU 占用最高的函数
ftrace 内核函数跟踪工具,分析函数执行时间、中断延迟 跟踪驱动的函数执行时间,定位瓶颈
cyclictest 测试系统的中断和调度延迟,验证实时性优化效果 cyclictest -m -t1 -p 99,测试最大延迟

2. 功耗测试工具

表格

工具 作用 用法
功率计 测量整机的实时功耗,看整机的平均功耗和峰值功耗 串在电源输入端,记录优化前后的功耗数据
powertop Linux 功耗分析工具,定位系统唤醒源、功耗高的模块 powertop,查看每个模块的功耗和唤醒次数
/sys/class/power_supply/ 查看电池的实时电流、电压、功耗 cat /sys/class/power_supply/battery/current_now,查看实时电流
kernelshark 可视化分析系统的唤醒、调度、中断事件,定位频繁唤醒的原因 配合 ftrace 使用,可视化分析唤醒源

结尾说两句

这篇文章,我们把驱动性能优化和功耗优化的核心方法,从代码级、内核级、系统级、硬件级,全部分享给你了,这些都是工业级量产产品必须掌握的技能。功能实现只是入门,能把驱动做到高性能、低功耗、高稳定,才是真正的资深驱动工程师。

下一篇,我们进入 RK 平台的核心加速模块,NPU / 硬件编解码驱动适配与安卓调用,教你怎么用 RK3568 的 NPU 做 AI 推理,用 VPU 做硬件编解码,充分发挥 RK 平台的硬件优势。

我是黒漂技术佬,关注我,带你零基础入门 RK 安卓驱动开发,不踩坑。有任何性能和功耗优化的问题,评论区留言,我都会一一回复。

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui