驱动性能优化技术¶
概述¶
在嵌入式系统开发中,驱动程序的性能直接影响整个系统的响应速度和吞吐量。随着应用需求的提升,如何优化驱动性能成为高级嵌入式工程师必须掌握的核心技能。
本教程将深入讲解驱动性能优化的各种技术,从性能分析方法到具体的优化策略,通过实战案例带你掌握构建高性能驱动系统的方法。
完成本教程后,你将能够:
- 掌握驱动性能分析和profiling方法
- 理解中断处理的性能瓶颈和优化策略
- 实现高效的DMA数据传输
- 设计合理的缓存和缓冲区策略
- 应用零拷贝技术减少数据搬运
- 优化驱动的CPU占用和延迟
- 进行系统级性能调优
背景知识¶
性能优化的基本原则¶
驱动性能优化需要遵循一些基本原则,避免盲目优化。
优化原则:
1. 先测量,后优化
- 使用profiling工具找出瓶颈
- 不要凭感觉优化
- 优化前后都要测量
2. 优化最热路径
- 20%的代码占用80%的时间
- 优先优化频繁执行的代码
- 冷路径可以牺牲性能换取可读性
3. 权衡取舍
- 性能 vs 可读性
- 性能 vs 内存占用
- 性能 vs 功耗
- 根据实际需求选择
4. 避免过早优化
- 先保证功能正确
- 再考虑性能优化
- 保持代码可维护性
性能指标:
| 指标 | 说明 | 测量方法 |
|---|---|---|
| 吞吐量 | 单位时间处理的数据量 | 字节/秒 |
| 延迟 | 从请求到响应的时间 | 微秒/毫秒 |
| CPU占用 | 驱动占用的CPU时间 | 百分比 |
| 中断频率 | 单位时间的中断次数 | 次/秒 |
| 内存占用 | 驱动使用的内存 | 字节 |
性能瓶颈分析¶
识别性能瓶颈是优化的第一步。
常见性能瓶颈:
1. 中断处理过长
- 中断服务函数执行时间过长
- 中断嵌套导致延迟增加
- 中断频率过高
2. 数据拷贝开销
- 多次内存拷贝
- CPU搬运数据
- 缓冲区管理不当
3. 同步开销
- 频繁的锁操作
- 忙等待
- 上下文切换
4. 缓存失效
- Cache miss率高
- 数据对齐问题
- 缓存行冲突
5. 轮询开销
- 忙等待浪费CPU
- 轮询间隔不合理
- 应该用中断的地方用了轮询
性能分析流程:
graph TD
A[识别性能问题] --> B[测量基准性能]
B --> C[使用profiling工具]
C --> D[定位热点代码]
D --> E[分析瓶颈原因]
E --> F[制定优化方案]
F --> G[实施优化]
G --> H[测量优化效果]
H --> I{达到目标?}
I -->|否| D
I -->|是| J[完成优化]
环境准备¶
硬件要求¶
- STM32F4系列开发板(如STM32F407VET6)
- 逻辑分析仪(用于测量时序)
- 示波器(用于观察信号)
- USB转TTL串口模块
- 高速存储设备(用于测试DMA)
软件要求¶
- Keil MDK 5.x 或 STM32CubeIDE
- STM32F4 HAL库或标准外设库
- 性能分析工具(如Keil Event Recorder)
- 串口调试工具
测试环境搭建¶
#include "stm32f4xx.h"
#include <stdio.h>
// 高精度计时器初始化(使用DWT)
void Profiler_Init(void) {
// 使能DWT
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
// 复位计数器
DWT->CYCCNT = 0;
// 使能计数器
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
// 获取当前时钟周期数
uint32_t Profiler_GetCycles(void) {
return DWT->CYCCNT;
}
// 计算执行时间(微秒)
float Profiler_GetTime_us(uint32_t cycles) {
// 假设系统时钟168MHz
return (float)cycles / 168.0f;
}
// 性能测试宏
#define PROFILE_START() uint32_t _start = Profiler_GetCycles()
#define PROFILE_END(name) do { \
uint32_t _end = Profiler_GetCycles(); \
uint32_t _cycles = _end - _start; \
printf("%s: %lu cycles (%.2f us)\r\n", name, _cycles, Profiler_GetTime_us(_cycles)); \
} while(0)
核心内容¶
步骤1:性能分析与测量¶
首先学习如何准确测量驱动性能。
/**
* @brief 性能统计结构体
*/
typedef struct {
uint32_t call_count; // 调用次数
uint32_t total_cycles; // 总周期数
uint32_t min_cycles; // 最小周期数
uint32_t max_cycles; // 最大周期数
uint32_t avg_cycles; // 平均周期数
} Perf_Stats_t;
/**
* @brief 性能统计器
*/
typedef struct {
const char *name; // 统计项名称
Perf_Stats_t stats; // 统计数据
uint32_t start_time; // 开始时间
} Perf_Counter_t;
#define MAX_PERF_COUNTERS 16
Perf_Counter_t perf_counters[MAX_PERF_COUNTERS];
uint8_t perf_counter_count = 0;
/**
* @brief 创建性能计数器
* @param name: 计数器名称
* @retval 计数器ID,-1表示失败
*/
int8_t Perf_CreateCounter(const char *name) {
if (perf_counter_count >= MAX_PERF_COUNTERS) {
return -1;
}
Perf_Counter_t *counter = &perf_counters[perf_counter_count];
counter->name = name;
counter->stats.call_count = 0;
counter->stats.total_cycles = 0;
counter->stats.min_cycles = 0xFFFFFFFF;
counter->stats.max_cycles = 0;
counter->stats.avg_cycles = 0;
counter->start_time = 0;
return perf_counter_count++;
}
/**
* @brief 开始性能测量
* @param counter_id: 计数器ID
* @retval 无
*/
void Perf_Start(int8_t counter_id) {
if (counter_id < 0 || counter_id >= perf_counter_count) {
return;
}
perf_counters[counter_id].start_time = Profiler_GetCycles();
}
/**
* @brief 结束性能测量
* @param counter_id: 计数器ID
* @retval 无
*/
void Perf_End(int8_t counter_id) {
uint32_t end_time = Profiler_GetCycles();
if (counter_id < 0 || counter_id >= perf_counter_count) {
return;
}
Perf_Counter_t *counter = &perf_counters[counter_id];
uint32_t elapsed = end_time - counter->start_time;
// 更新统计
counter->stats.call_count++;
counter->stats.total_cycles += elapsed;
if (elapsed < counter->stats.min_cycles) {
counter->stats.min_cycles = elapsed;
}
if (elapsed > counter->stats.max_cycles) {
counter->stats.max_cycles = elapsed;
}
counter->stats.avg_cycles = counter->stats.total_cycles / counter->stats.call_count;
}
/**
* @brief 打印性能统计
* @param counter_id: 计数器ID
* @retval 无
*/
void Perf_Print(int8_t counter_id) {
if (counter_id < 0 || counter_id >= perf_counter_count) {
return;
}
Perf_Counter_t *counter = &perf_counters[counter_id];
Perf_Stats_t *stats = &counter->stats;
printf("=== %s ===\r\n", counter->name);
printf("Calls: %lu\r\n", stats->call_count);
printf("Total: %lu cycles (%.2f ms)\r\n",
stats->total_cycles, Profiler_GetTime_us(stats->total_cycles) / 1000.0f);
printf("Avg: %lu cycles (%.2f us)\r\n",
stats->avg_cycles, Profiler_GetTime_us(stats->avg_cycles));
printf("Min: %lu cycles (%.2f us)\r\n",
stats->min_cycles, Profiler_GetTime_us(stats->min_cycles));
printf("Max: %lu cycles (%.2f us)\r\n",
stats->max_cycles, Profiler_GetTime_us(stats->max_cycles));
printf("================\r\n");
}
/**
* @brief 打印所有性能统计
* @param 无
* @retval 无
*/
void Perf_PrintAll(void) {
printf("\r\n=== Performance Statistics ===\r\n");
for (int i = 0; i < perf_counter_count; i++) {
Perf_Print(i);
}
}
/**
* @brief 重置性能统计
* @param counter_id: 计数器ID
* @retval 无
*/
void Perf_Reset(int8_t counter_id) {
if (counter_id < 0 || counter_id >= perf_counter_count) {
return;
}
Perf_Counter_t *counter = &perf_counters[counter_id];
counter->stats.call_count = 0;
counter->stats.total_cycles = 0;
counter->stats.min_cycles = 0xFFFFFFFF;
counter->stats.max_cycles = 0;
counter->stats.avg_cycles = 0;
}
使用示例:
// 创建性能计数器
int8_t uart_tx_counter = Perf_CreateCounter("UART TX");
int8_t dma_copy_counter = Perf_CreateCounter("DMA Copy");
void test_performance(void) {
uint8_t data[1024];
// 测试UART发送
Perf_Start(uart_tx_counter);
UART_Send(data, 1024);
Perf_End(uart_tx_counter);
// 测试DMA拷贝
Perf_Start(dma_copy_counter);
DMA_MemCopy(data, buffer, 1024);
Perf_End(dma_copy_counter);
// 打印统计
Perf_PrintAll();
}
步骤2:中断优化¶
中断处理是驱动性能的关键,需要精心优化。
/**
* @brief 中断延迟测量
*/
typedef struct {
uint32_t request_time; // 中断请求时间
uint32_t enter_time; // 进入ISR时间
uint32_t exit_time; // 退出ISR时间
uint32_t latency; // 中断延迟
uint32_t duration; // ISR执行时间
} IRQ_Timing_t;
volatile IRQ_Timing_t irq_timing;
/**
* @brief 测量中断延迟(在中断服务函数开始处调用)
* @param 无
* @retval 无
*/
void IRQ_MeasureLatency(void) {
irq_timing.enter_time = Profiler_GetCycles();
irq_timing.latency = irq_timing.enter_time - irq_timing.request_time;
}
/**
* @brief 测量ISR执行时间(在中断服务函数结束处调用)
* @param 无
* @retval 无
*/
void IRQ_MeasureDuration(void) {
irq_timing.exit_time = Profiler_GetCycles();
irq_timing.duration = irq_timing.exit_time - irq_timing.enter_time;
}
/**
* @brief 优化前的中断服务函数(示例)
*/
void USART1_IRQHandler_Slow(void) {
IRQ_MeasureLatency();
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
// 问题1:在ISR中进行复杂处理
process_data(data); // 耗时操作
// 问题2:在ISR中进行I/O操作
printf("Received: %02X\r\n", data); // 非常慢
// 问题3:在ISR中等待
while (!(USART2->SR & USART_SR_TXE)); // 忙等待
USART2->DR = data;
}
IRQ_MeasureDuration();
}
/**
* @brief 优化后的中断服务函数
*/
#define RX_BUFFER_SIZE 256
uint8_t rx_buffer[RX_BUFFER_SIZE];
volatile uint16_t rx_head = 0;
volatile uint16_t rx_tail = 0;
volatile uint8_t data_ready = 0;
void USART1_IRQHandler_Fast(void) {
IRQ_MeasureLatency();
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
// 优化1:只做最必要的操作
rx_buffer[rx_head] = data;
rx_head = (rx_head + 1) % RX_BUFFER_SIZE;
// 优化2:设置标志,让主循环处理
data_ready = 1;
// 优化3:避免在ISR中等待
// 数据处理移到主循环
}
IRQ_MeasureDuration();
}
/**
* @brief 主循环中的数据处理
*/
void main_loop_process(void) {
if (data_ready) {
data_ready = 0;
// 在主循环中处理数据
while (rx_tail != rx_head) {
uint8_t data = rx_buffer[rx_tail];
rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE;
// 复杂处理
process_data(data);
// I/O操作
printf("Received: %02X\r\n", data);
}
}
}
中断优化原则:
1. ISR要短小精悍
- 只做最必要的操作
- 读取数据、清除标志、设置标志
- 复杂处理移到主循环或任务
2. 避免在ISR中等待
- 不要忙等待
- 不要调用可能阻塞的函数
- 不要进行I/O操作
3. 减少中断嵌套
- 合理设置中断优先级
- 避免低优先级中断被频繁打断
- 关键中断使用高优先级
4. 使用DMA减少中断
- 数据传输用DMA
- 减少中断频率
- 降低CPU负担
中断合并技术:
/**
* @brief 中断合并示例
*/
#define BATCH_SIZE 16
uint8_t batch_buffer[BATCH_SIZE];
volatile uint8_t batch_count = 0;
void USART1_IRQHandler_Batched(void) {
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
// 累积数据
batch_buffer[batch_count++] = data;
// 达到批量大小或超时时才处理
if (batch_count >= BATCH_SIZE) {
// 设置标志,批量处理
data_ready = 1;
batch_count = 0;
}
}
}
/**
* @brief 批量处理数据
*/
void process_batch(void) {
if (data_ready) {
data_ready = 0;
// 一次处理多个数据
for (int i = 0; i < BATCH_SIZE; i++) {
process_data(batch_buffer[i]);
}
}
}
步骤3:DMA优化¶
DMA是提高数据传输效率的关键技术。
/**
* @brief DMA传输优化配置
*/
typedef struct {
uint8_t use_fifo; // 使用FIFO模式
uint8_t burst_mode; // 突发传输模式
uint8_t double_buffer; // 双缓冲模式
uint8_t priority; // 优先级
uint32_t threshold; // FIFO阈值
} DMA_OptConfig_t;
/**
* @brief 配置DMA FIFO模式
* @param stream: DMA流
* @param threshold: FIFO阈值
* @retval 无
*/
void DMA_ConfigFIFO(DMA_Stream_TypeDef *stream, uint32_t threshold) {
// 使能FIFO模式
stream->FCR |= DMA_SxFCR_DMDIS;
// 配置FIFO阈值
stream->FCR &= ~DMA_SxFCR_FTH;
stream->FCR |= threshold;
/*
* FIFO阈值选项:
* 0: 1/4 full
* 1: 1/2 full
* 2: 3/4 full
* 3: full
*/
}
/**
* @brief 配置DMA突发传输
* @param stream: DMA流
* @param mburst: 内存突发大小
* @param pburst: 外设突发大小
* @retval 无
*/
void DMA_ConfigBurst(DMA_Stream_TypeDef *stream, uint32_t mburst, uint32_t pburst) {
stream->CR &= ~(DMA_SxCR_MBURST | DMA_SxCR_PBURST);
stream->CR |= (mburst << 23) | (pburst << 21);
/*
* 突发大小选项:
* 0: 单次传输
* 1: 4次传输
* 2: 8次传输
* 3: 16次传输
*/
}
/**
* @brief 配置DMA双缓冲模式
* @param stream: DMA流
* @param buffer0: 缓冲区0地址
* @param buffer1: 缓冲区1地址
* @param size: 缓冲区大小
* @retval 无
*/
void DMA_ConfigDoubleBuffer(DMA_Stream_TypeDef *stream,
uint32_t buffer0, uint32_t buffer1, uint32_t size) {
// 禁用DMA
stream->CR &= ~DMA_SxCR_EN;
while (stream->CR & DMA_SxCR_EN);
// 配置双缓冲模式
stream->CR |= DMA_SxCR_DBM;
// 配置两个缓冲区
stream->M0AR = buffer0;
stream->M1AR = buffer1;
stream->NDTR = size;
// 使能传输完成中断
stream->CR |= DMA_SxCR_TCIE;
}
/**
* @brief 优化的DMA内存拷贝
* @param src: 源地址
* @param dst: 目标地址
* @param size: 大小(字节)
* @retval 无
*/
void DMA_MemCopy_Optimized(const void *src, void *dst, uint32_t size) {
DMA_Stream_TypeDef *stream = DMA2_Stream0;
// 禁用DMA
stream->CR &= ~DMA_SxCR_EN;
while (stream->CR & DMA_SxCR_EN);
// 配置为内存到内存传输
stream->CR = 0;
stream->CR |= (2 << 6); // 内存到内存
// 优化1:使用32位传输(如果对齐)
if (((uint32_t)src % 4 == 0) && ((uint32_t)dst % 4 == 0) && (size % 4 == 0)) {
stream->CR |= (2 << 11) | (2 << 13); // 32位宽度
size /= 4;
} else {
stream->CR |= (0 << 11) | (0 << 13); // 8位宽度
}
// 优化2:使用FIFO模式
stream->FCR |= DMA_SxFCR_DMDIS;
stream->FCR &= ~DMA_SxFCR_FTH;
stream->FCR |= (3 << 0); // Full FIFO
// 优化3:使用突发传输
stream->CR |= (3 << 23) | (3 << 21); // 16次突发
// 优化4:最高优先级
stream->CR |= (3 << 16);
// 配置地址递增
stream->CR |= DMA_SxCR_PINC | DMA_SxCR_MINC;
// 配置地址和大小
stream->PAR = (uint32_t)src;
stream->M0AR = (uint32_t)dst;
stream->NDTR = size;
// 启动传输
stream->CR |= DMA_SxCR_EN;
// 等待完成
while (stream->CR & DMA_SxCR_EN);
}
/**
* @brief DMA性能对比测试
*/
void DMA_PerformanceTest(void) {
#define TEST_SIZE 4096
uint8_t src[TEST_SIZE] __attribute__((aligned(4)));
uint8_t dst[TEST_SIZE] __attribute__((aligned(4)));
// 初始化数据
for (int i = 0; i < TEST_SIZE; i++) {
src[i] = i & 0xFF;
}
printf("DMA Performance Test (%d bytes)\r\n", TEST_SIZE);
// 测试1:标准memcpy
PROFILE_START();
memcpy(dst, src, TEST_SIZE);
PROFILE_END("memcpy");
// 测试2:DMA直接模式
PROFILE_START();
DMA_MemCopy_Direct(src, dst, TEST_SIZE);
PROFILE_END("DMA Direct");
// 测试3:DMA FIFO模式
PROFILE_START();
DMA_MemCopy_FIFO(src, dst, TEST_SIZE);
PROFILE_END("DMA FIFO");
// 测试4:DMA优化模式
PROFILE_START();
DMA_MemCopy_Optimized(src, dst, TEST_SIZE);
PROFILE_END("DMA Optimized");
}
DMA优化技巧:
1. 使用FIFO模式
- 减少总线占用
- 提高传输效率
- 适合高速外设
2. 使用突发传输
- 减少总线仲裁次数
- 提高带宽利用率
- 适合大块数据传输
3. 数据对齐
- 使用32位传输
- 提高传输速度
- 减少传输次数
4. 双缓冲模式
- 连续数据流
- 避免数据丢失
- 提高吞吐量
5. 合理设置优先级
- 关键传输高优先级
- 避免优先级反转
- 平衡各DMA通道
步骤4:缓存策略优化¶
合理的缓存策略可以大幅提高性能。
/**
* @brief 环形缓冲区实现
*/
typedef struct {
uint8_t *buffer; // 缓冲区指针
uint32_t size; // 缓冲区大小
volatile uint32_t head; // 写指针
volatile uint32_t tail; // 读指针
volatile uint32_t count; // 数据量
} RingBuffer_t;
/**
* @brief 初始化环形缓冲区
* @param rb: 环形缓冲区指针
* @param buffer: 缓冲区
* @param size: 大小
* @retval 无
*/
void RingBuffer_Init(RingBuffer_t *rb, uint8_t *buffer, uint32_t size) {
rb->buffer = buffer;
rb->size = size;
rb->head = 0;
rb->tail = 0;
rb->count = 0;
}
/**
* @brief 写入数据到环形缓冲区
* @param rb: 环形缓冲区指针
* @param data: 数据指针
* @param len: 长度
* @retval 实际写入的字节数
*/
uint32_t RingBuffer_Write(RingBuffer_t *rb, const uint8_t *data, uint32_t len) {
uint32_t free_space = rb->size - rb->count;
uint32_t to_write = (len < free_space) ? len : free_space;
for (uint32_t i = 0; i < to_write; i++) {
rb->buffer[rb->head] = data[i];
rb->head = (rb->head + 1) % rb->size;
}
__disable_irq();
rb->count += to_write;
__enable_irq();
return to_write;
}
/**
* @brief 从环形缓冲区读取数据
* @param rb: 环形缓冲区指针
* @param data: 数据指针
* @param len: 长度
* @retval 实际读取的字节数
*/
uint32_t RingBuffer_Read(RingBuffer_t *rb, uint8_t *data, uint32_t len) {
uint32_t to_read = (len < rb->count) ? len : rb->count;
for (uint32_t i = 0; i < to_read; i++) {
data[i] = rb->buffer[rb->tail];
rb->tail = (rb->tail + 1) % rb->size;
}
__disable_irq();
rb->count -= to_read;
__enable_irq();
return to_read;
}
/**
* @brief 优化的环形缓冲区(零拷贝)
*/
typedef struct {
uint8_t *buffer;
uint32_t size;
volatile uint32_t head;
volatile uint32_t tail;
} RingBuffer_ZeroCopy_t;
/**
* @brief 获取可写区域
* @param rb: 环形缓冲区指针
* @param ptr: 返回可写区域指针
* @param len: 返回可写长度
* @retval 无
*/
void RingBuffer_GetWriteRegion(RingBuffer_ZeroCopy_t *rb, uint8_t **ptr, uint32_t *len) {
*ptr = &rb->buffer[rb->head];
if (rb->head >= rb->tail) {
*len = rb->size - rb->head;
if (rb->tail == 0) {
(*len)--; // 避免head追上tail
}
} else {
*len = rb->tail - rb->head - 1;
}
}
/**
* @brief 提交写入
* @param rb: 环形缓冲区指针
* @param len: 写入长度
* @retval 无
*/
void RingBuffer_CommitWrite(RingBuffer_ZeroCopy_t *rb, uint32_t len) {
rb->head = (rb->head + len) % rb->size;
}
/**
* @brief 获取可读区域
* @param rb: 环形缓冲区指针
* @param ptr: 返回可读区域指针
* @param len: 返回可读长度
* @retval 无
*/
void RingBuffer_GetReadRegion(RingBuffer_ZeroCopy_t *rb, uint8_t **ptr, uint32_t *len) {
*ptr = &rb->buffer[rb->tail];
if (rb->tail < rb->head) {
*len = rb->head - rb->tail;
} else {
*len = rb->size - rb->tail;
}
}
/**
* @brief 提交读取
* @param rb: 环形缓冲区指针
* @param len: 读取长度
* @retval 无
*/
void RingBuffer_CommitRead(RingBuffer_ZeroCopy_t *rb, uint32_t len) {
rb->tail = (rb->tail + len) % rb->size;
}
/**
* @brief 内存池实现
*/
#define POOL_BLOCK_SIZE 128
#define POOL_BLOCK_COUNT 16
typedef struct {
uint8_t data[POOL_BLOCK_SIZE];
uint8_t in_use;
} MemBlock_t;
typedef struct {
MemBlock_t blocks[POOL_BLOCK_COUNT];
uint32_t alloc_count;
uint32_t free_count;
} MemPool_t;
MemPool_t mem_pool;
/**
* @brief 初始化内存池
* @param 无
* @retval 无
*/
void MemPool_Init(void) {
for (int i = 0; i < POOL_BLOCK_COUNT; i++) {
mem_pool.blocks[i].in_use = 0;
}
mem_pool.alloc_count = 0;
mem_pool.free_count = 0;
}
/**
* @brief 从内存池分配内存
* @param 无
* @retval 内存块指针,NULL表示失败
*/
void *MemPool_Alloc(void) {
for (int i = 0; i < POOL_BLOCK_COUNT; i++) {
if (!mem_pool.blocks[i].in_use) {
mem_pool.blocks[i].in_use = 1;
mem_pool.alloc_count++;
return mem_pool.blocks[i].data;
}
}
return NULL; // 内存池已满
}
/**
* @brief 释放内存到内存池
* @param ptr: 内存块指针
* @retval 无
*/
void MemPool_Free(void *ptr) {
for (int i = 0; i < POOL_BLOCK_COUNT; i++) {
if (mem_pool.blocks[i].data == ptr) {
mem_pool.blocks[i].in_use = 0;
mem_pool.free_count++;
return;
}
}
}
/**
* @brief 获取内存池统计
* @param 无
* @retval 无
*/
void MemPool_PrintStats(void) {
uint32_t used = 0;
for (int i = 0; i < POOL_BLOCK_COUNT; i++) {
if (mem_pool.blocks[i].in_use) {
used++;
}
}
printf("Memory Pool Stats:\r\n");
printf("Total: %d blocks\r\n", POOL_BLOCK_COUNT);
printf("Used: %lu blocks\r\n", used);
printf("Free: %lu blocks\r\n", POOL_BLOCK_COUNT - used);
printf("Alloc Count: %lu\r\n", mem_pool.alloc_count);
printf("Free Count: %lu\r\n", mem_pool.free_count);
}
缓存优化策略:
1. 使用环形缓冲区
- 避免数据搬移
- 高效的FIFO实现
- 适合流式数据
2. 零拷贝技术
- 直接操作缓冲区
- 避免memcpy
- 提高吞吐量
3. 内存池
- 避免频繁malloc/free
- 减少内存碎片
- 提高分配速度
4. 预分配缓冲区
- 启动时分配
- 避免运行时分配
- 提高实时性
5. 缓冲区大小优化
- 根据数据速率调整
- 避免过大或过小
- 平衡内存和性能
步骤5:零拷贝技术¶
零拷贝技术可以消除不必要的数据拷贝,大幅提高性能。
/**
* @brief 零拷贝UART发送
*/
typedef struct {
const uint8_t *data; // 数据指针
uint32_t length; // 数据长度
uint32_t sent; // 已发送字节数
void (*callback)(void); // 完成回调
} UART_TxRequest_t;
#define TX_QUEUE_SIZE 8
UART_TxRequest_t tx_queue[TX_QUEUE_SIZE];
volatile uint8_t tx_head = 0;
volatile uint8_t tx_tail = 0;
volatile uint8_t tx_busy = 0;
/**
* @brief 零拷贝发送(不拷贝数据,直接使用用户缓冲区)
* @param data: 数据指针(必须保持有效直到发送完成)
* @param length: 长度
* @param callback: 完成回调
* @retval 0=成功,1=队列满
*/
uint8_t UART_SendZeroCopy(const uint8_t *data, uint32_t length, void (*callback)(void)) {
// 检查队列是否满
uint8_t next_head = (tx_head + 1) % TX_QUEUE_SIZE;
if (next_head == tx_tail) {
return 1; // 队列满
}
// 添加到队列
tx_queue[tx_head].data = data;
tx_queue[tx_head].length = length;
tx_queue[tx_head].sent = 0;
tx_queue[tx_head].callback = callback;
tx_head = next_head;
// 如果当前空闲,启动发送
if (!tx_busy) {
UART_StartNextTransmit();
}
return 0;
}
/**
* @brief 启动下一个传输
* @param 无
* @retval 无
*/
void UART_StartNextTransmit(void) {
if (tx_tail == tx_head) {
tx_busy = 0;
return; // 队列空
}
tx_busy = 1;
UART_TxRequest_t *req = &tx_queue[tx_tail];
// 配置DMA直接从用户缓冲区发送
DMA2_Stream7->M0AR = (uint32_t)req->data;
DMA2_Stream7->NDTR = req->length;
DMA2_Stream7->CR |= DMA_SxCR_EN;
}
/**
* @brief DMA发送完成中断
* @param 无
* @retval 无
*/
void DMA2_Stream7_IRQHandler(void) {
if (DMA2->HISR & DMA_HISR_TCIF7) {
DMA2->HIFCR = DMA_HIFCR_CTCIF7;
// 调用回调
UART_TxRequest_t *req = &tx_queue[tx_tail];
if (req->callback) {
req->callback();
}
// 移到下一个
tx_tail = (tx_tail + 1) % TX_QUEUE_SIZE;
// 启动下一个传输
UART_StartNextTransmit();
}
}
/**
* @brief 零拷贝接收(直接写入用户缓冲区)
*/
typedef struct {
uint8_t *buffer; // 接收缓冲区
uint32_t size; // 缓冲区大小
volatile uint32_t received; // 已接收字节数
void (*callback)(uint32_t len); // 完成回调
} UART_RxRequest_t;
UART_RxRequest_t rx_request;
/**
* @brief 零拷贝接收
* @param buffer: 接收缓冲区
* @param size: 缓冲区大小
* @param callback: 完成回调
* @retval 0=成功,1=失败
*/
uint8_t UART_ReceiveZeroCopy(uint8_t *buffer, uint32_t size,
void (*callback)(uint32_t len)) {
if (rx_request.buffer != NULL) {
return 1; // 已有接收请求
}
rx_request.buffer = buffer;
rx_request.size = size;
rx_request.received = 0;
rx_request.callback = callback;
// 配置DMA直接写入用户缓冲区
DMA2_Stream5->M0AR = (uint32_t)buffer;
DMA2_Stream5->NDTR = size;
DMA2_Stream5->CR |= DMA_SxCR_EN;
// 使能IDLE中断
USART1->CR1 |= USART_CR1_IDLEIE;
return 0;
}
/**
* @brief UART IDLE中断(接收完成)
* @param 无
* @retval 无
*/
void USART1_IRQHandler(void) {
if (USART1->SR & USART_SR_IDLE) {
// 清除IDLE标志
volatile uint32_t temp = USART1->SR;
temp = USART1->DR;
(void)temp;
// 停止DMA
DMA2_Stream5->CR &= ~DMA_SxCR_EN;
// 计算接收到的字节数
uint32_t received = rx_request.size - DMA2_Stream5->NDTR;
// 调用回调
if (rx_request.callback) {
rx_request.callback(received);
}
// 清除请求
rx_request.buffer = NULL;
}
}
/**
* @brief scatter-gather DMA(分散-聚集)
*/
typedef struct {
uint32_t addr; // 地址
uint32_t length; // 长度
} DMA_Segment_t;
/**
* @brief scatter-gather传输
* @param segments: 段数组
* @param count: 段数量
* @retval 无
*/
void DMA_ScatterGather(DMA_Segment_t *segments, uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
// 配置DMA传输每个段
DMA2_Stream0->M0AR = segments[i].addr;
DMA2_Stream0->NDTR = segments[i].length;
DMA2_Stream0->CR |= DMA_SxCR_EN;
// 等待完成
while (DMA2_Stream0->CR & DMA_SxCR_EN);
}
}
零拷贝优化效果:
传统方式:
用户缓冲区 -> 驱动缓冲区 -> DMA -> 外设
拷贝次数:2次
CPU占用:高
零拷贝方式:
用户缓冲区 -> DMA -> 外设
拷贝次数:0次
CPU占用:低
性能提升:
- 减少CPU占用50%以上
- 提高吞吐量30-50%
- 降低延迟
步骤6:CPU占用优化¶
降低驱动的CPU占用,释放资源给应用。
/**
* @brief CPU占用率测量
*/
typedef struct {
uint32_t idle_count; // 空闲计数
uint32_t total_count; // 总计数
uint32_t cpu_usage; // CPU占用率(百分比)
} CPU_Usage_t;
CPU_Usage_t cpu_usage;
/**
* @brief CPU占用率测量初始化
* @param 无
* @retval 无
*/
void CPU_Usage_Init(void) {
cpu_usage.idle_count = 0;
cpu_usage.total_count = 0;
cpu_usage.cpu_usage = 0;
}
/**
* @brief 空闲任务(在主循环中调用)
* @param 无
* @retval 无
*/
void CPU_IdleTask(void) {
cpu_usage.idle_count++;
}
/**
* @brief 计算CPU占用率(定时调用,如每秒)
* @param 无
* @retval CPU占用率(0-100)
*/
uint32_t CPU_GetUsage(void) {
static uint32_t last_idle = 0;
static uint32_t last_total = 0;
uint32_t idle_delta = cpu_usage.idle_count - last_idle;
uint32_t total_delta = cpu_usage.total_count - last_total;
if (total_delta > 0) {
cpu_usage.cpu_usage = 100 - (idle_delta * 100 / total_delta);
}
last_idle = cpu_usage.idle_count;
last_total = cpu_usage.total_count;
return cpu_usage.cpu_usage;
}
/**
* @brief 轮询优化:自适应轮询间隔
*/
typedef struct {
uint32_t min_interval; // 最小间隔(us)
uint32_t max_interval; // 最大间隔(us)
uint32_t current_interval;// 当前间隔
uint32_t hit_count; // 命中计数
uint32_t miss_count; // 未命中计数
} AdaptivePoll_t;
/**
* @brief 初始化自适应轮询
* @param poll: 轮询结构体
* @param min_interval: 最小间隔
* @param max_interval: 最大间隔
* @retval 无
*/
void AdaptivePoll_Init(AdaptivePoll_t *poll, uint32_t min_interval, uint32_t max_interval) {
poll->min_interval = min_interval;
poll->max_interval = max_interval;
poll->current_interval = min_interval;
poll->hit_count = 0;
poll->miss_count = 0;
}
/**
* @brief 自适应轮询
* @param poll: 轮询结构体
* @param has_data: 是否有数据
* @retval 无
*/
void AdaptivePoll_Update(AdaptivePoll_t *poll, uint8_t has_data) {
if (has_data) {
poll->hit_count++;
// 有数据,减小间隔
if (poll->current_interval > poll->min_interval) {
poll->current_interval = poll->current_interval * 9 / 10;
if (poll->current_interval < poll->min_interval) {
poll->current_interval = poll->min_interval;
}
}
} else {
poll->miss_count++;
// 无数据,增大间隔
if (poll->current_interval < poll->max_interval) {
poll->current_interval = poll->current_interval * 11 / 10;
if (poll->current_interval > poll->max_interval) {
poll->current_interval = poll->max_interval;
}
}
}
}
/**
* @brief 获取当前轮询间隔
* @param poll: 轮询结构体
* @retval 轮询间隔(us)
*/
uint32_t AdaptivePoll_GetInterval(AdaptivePoll_t *poll) {
return poll->current_interval;
}
/**
* @brief 使用示例:自适应轮询UART
*/
AdaptivePoll_t uart_poll;
void uart_adaptive_poll_example(void) {
AdaptivePoll_Init(&uart_poll, 100, 10000); // 100us - 10ms
while (1) {
// 检查是否有数据
uint8_t has_data = (USART1->SR & USART_SR_RXNE) ? 1 : 0;
if (has_data) {
uint8_t data = USART1->DR;
process_data(data);
}
// 更新轮询间隔
AdaptivePoll_Update(&uart_poll, has_data);
// 延时
Delay_us(AdaptivePoll_GetInterval(&uart_poll));
}
}
/**
* @brief 批量处理优化
*/
#define BATCH_THRESHOLD 16
uint8_t batch_buffer[256];
uint32_t batch_count = 0;
/**
* @brief 批量处理数据
* @param data: 数据
* @retval 无
*/
void process_data_batched(uint8_t data) {
// 累积数据
batch_buffer[batch_count++] = data;
// 达到阈值时批量处理
if (batch_count >= BATCH_THRESHOLD) {
// 一次处理多个数据
for (uint32_t i = 0; i < batch_count; i++) {
// 处理逻辑
process_single_data(batch_buffer[i]);
}
batch_count = 0;
}
}
/**
* @brief 延迟处理优化
*/
typedef struct {
void (*func)(void *arg); // 处理函数
void *arg; // 参数
uint32_t delay; // 延迟时间(ms)
uint32_t timestamp; // 时间戳
} DeferredWork_t;
#define MAX_DEFERRED_WORKS 16
DeferredWork_t deferred_works[MAX_DEFERRED_WORKS];
uint8_t deferred_work_count = 0;
/**
* @brief 添加延迟处理
* @param func: 处理函数
* @param arg: 参数
* @param delay: 延迟时间(ms)
* @retval 0=成功,1=失败
*/
uint8_t DeferredWork_Add(void (*func)(void *arg), void *arg, uint32_t delay) {
if (deferred_work_count >= MAX_DEFERRED_WORKS) {
return 1;
}
DeferredWork_t *work = &deferred_works[deferred_work_count++];
work->func = func;
work->arg = arg;
work->delay = delay;
work->timestamp = GetTick();
return 0;
}
/**
* @brief 处理延迟任务(在主循环中调用)
* @param 无
* @retval 无
*/
void DeferredWork_Process(void) {
uint32_t now = GetTick();
for (uint8_t i = 0; i < deferred_work_count; i++) {
DeferredWork_t *work = &deferred_works[i];
if (now - work->timestamp >= work->delay) {
// 执行任务
work->func(work->arg);
// 移除任务
for (uint8_t j = i; j < deferred_work_count - 1; j++) {
deferred_works[j] = deferred_works[j + 1];
}
deferred_work_count--;
i--;
}
}
}
CPU占用优化策略:
1. 减少轮询
- 使用中断代替轮询
- 自适应轮询间隔
- 只在必要时轮询
2. 批量处理
- 累积数据批量处理
- 减少函数调用开销
- 提高缓存命中率
3. 延迟处理
- 非紧急任务延迟处理
- 在空闲时处理
- 平滑CPU负载
4. 使用DMA
- 数据传输用DMA
- 减少CPU干预
- 释放CPU资源
5. 优化算法
- 使用高效算法
- 避免不必要的计算
- 缓存计算结果
实践示例¶
示例1:高性能UART驱动¶
综合应用各种优化技术实现高性能UART驱动。
/**
* @brief 高性能UART驱动
*/
typedef struct {
USART_TypeDef *uart;
DMA_Stream_TypeDef *tx_dma;
DMA_Stream_TypeDef *rx_dma;
// 发送
RingBuffer_ZeroCopy_t tx_ring;
uint8_t tx_buffer[2048];
volatile uint8_t tx_busy;
// 接收
RingBuffer_ZeroCopy_t rx_ring;
uint8_t rx_buffer[2048];
uint8_t rx_dma_buffer[256];
// 统计
uint32_t tx_bytes;
uint32_t rx_bytes;
uint32_t tx_errors;
uint32_t rx_errors;
} HighPerf_UART_t;
HighPerf_UART_t hp_uart;
/**
* @brief 初始化高性能UART
* @param 无
* @retval 无
*/
void HighPerf_UART_Init(void) {
// 初始化UART硬件
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
// 配置UART
USART1->BRR = 84000000 / 115200;
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
USART1->CR3 = USART_CR3_DMAT | USART_CR3_DMAR;
// 初始化环形缓冲区
hp_uart.tx_ring.buffer = hp_uart.tx_buffer;
hp_uart.tx_ring.size = sizeof(hp_uart.tx_buffer);
hp_uart.tx_ring.head = 0;
hp_uart.tx_ring.tail = 0;
hp_uart.rx_ring.buffer = hp_uart.rx_buffer;
hp_uart.rx_ring.size = sizeof(hp_uart.rx_buffer);
hp_uart.rx_ring.head = 0;
hp_uart.rx_ring.tail = 0;
// 配置DMA TX
DMA2_Stream7->CR = 0;
DMA2_Stream7->CR |= (4 << 25); // Channel 4
DMA2_Stream7->CR |= (1 << 6); // 内存到外设
DMA2_Stream7->CR |= DMA_SxCR_MINC;
DMA2_Stream7->CR |= (1 << 16); // 中等优先级
DMA2_Stream7->PAR = (uint32_t)&USART1->DR;
// 配置DMA RX(循环模式)
DMA2_Stream5->CR = 0;
DMA2_Stream5->CR |= (4 << 25); // Channel 4
DMA2_Stream5->CR |= (0 << 6); // 外设到内存
DMA2_Stream5->CR |= DMA_SxCR_MINC;
DMA2_Stream5->CR |= DMA_SxCR_CIRC; // 循环模式
DMA2_Stream5->CR |= (2 << 16); // 高优先级
DMA2_Stream5->PAR = (uint32_t)&USART1->DR;
DMA2_Stream5->M0AR = (uint32_t)hp_uart.rx_dma_buffer;
DMA2_Stream5->NDTR = sizeof(hp_uart.rx_dma_buffer);
DMA2_Stream5->CR |= DMA_SxCR_EN;
// 使能中断
DMA2_Stream7->CR |= DMA_SxCR_TCIE;
USART1->CR1 |= USART_CR1_IDLEIE;
NVIC_EnableIRQ(DMA2_Stream7_IRQn);
NVIC_EnableIRQ(USART1_IRQn);
hp_uart.tx_busy = 0;
}
/**
* @brief 发送数据(零拷贝)
* @param data: 数据指针
* @param len: 长度
* @retval 实际写入的字节数
*/
uint32_t HighPerf_UART_Send(const uint8_t *data, uint32_t len) {
// 写入环形缓冲区
uint32_t written = 0;
while (written < len) {
uint8_t *ptr;
uint32_t available;
RingBuffer_GetWriteRegion(&hp_uart.tx_ring, &ptr, &available);
if (available == 0) {
break; // 缓冲区满
}
uint32_t to_write = (len - written < available) ? (len - written) : available;
memcpy(ptr, data + written, to_write);
RingBuffer_CommitWrite(&hp_uart.tx_ring, to_write);
written += to_write;
}
// 如果DMA空闲,启动发送
if (!hp_uart.tx_busy) {
HighPerf_UART_StartTx();
}
return written;
}
/**
* @brief 启动DMA发送
* @param 无
* @retval 无
*/
void HighPerf_UART_StartTx(void) {
uint8_t *ptr;
uint32_t len;
RingBuffer_GetReadRegion(&hp_uart.tx_ring, &ptr, &len);
if (len == 0) {
hp_uart.tx_busy = 0;
return;
}
hp_uart.tx_busy = 1;
// 配置DMA直接从环形缓冲区发送
DMA2_Stream7->M0AR = (uint32_t)ptr;
DMA2_Stream7->NDTR = len;
DMA2_Stream7->CR |= DMA_SxCR_EN;
}
/**
* @brief DMA发送完成中断
* @param 无
* @retval 无
*/
void DMA2_Stream7_IRQHandler(void) {
if (DMA2->HISR & DMA_HISR_TCIF7) {
DMA2->HIFCR = DMA_HIFCR_CTCIF7;
// 更新环形缓冲区
uint32_t sent = 256 - DMA2_Stream7->NDTR;
RingBuffer_CommitRead(&hp_uart.tx_ring, sent);
hp_uart.tx_bytes += sent;
// 启动下一次发送
HighPerf_UART_StartTx();
}
}
/**
* @brief 接收数据
* @param data: 数据缓冲区
* @param len: 缓冲区大小
* @retval 实际读取的字节数
*/
uint32_t HighPerf_UART_Receive(uint8_t *data, uint32_t len) {
uint32_t read = 0;
while (read < len) {
uint8_t *ptr;
uint32_t available;
RingBuffer_GetReadRegion(&hp_uart.rx_ring, &ptr, &available);
if (available == 0) {
break; // 无数据
}
uint32_t to_read = (len - read < available) ? (len - read) : available;
memcpy(data + read, ptr, to_read);
RingBuffer_CommitRead(&hp_uart.rx_ring, to_read);
read += to_read;
}
return read;
}
/**
* @brief UART IDLE中断(处理接收)
* @param 无
* @retval 无
*/
void USART1_IRQHandler(void) {
if (USART1->SR & USART_SR_IDLE) {
volatile uint32_t temp = USART1->SR;
temp = USART1->DR;
(void)temp;
// 计算接收到的数据量
uint32_t received = sizeof(hp_uart.rx_dma_buffer) - DMA2_Stream5->NDTR;
// 拷贝到环形缓冲区
uint8_t *ptr;
uint32_t available;
RingBuffer_GetWriteRegion(&hp_uart.rx_ring, &ptr, &available);
if (available >= received) {
memcpy(ptr, hp_uart.rx_dma_buffer, received);
RingBuffer_CommitWrite(&hp_uart.rx_ring, received);
hp_uart.rx_bytes += received;
} else {
hp_uart.rx_errors++;
}
}
}
/**
* @brief 获取性能统计
* @param 无
* @retval 无
*/
void HighPerf_UART_PrintStats(void) {
printf("UART Performance Stats:\r\n");
printf("TX: %lu bytes\r\n", hp_uart.tx_bytes);
printf("RX: %lu bytes\r\n", hp_uart.rx_bytes);
printf("TX Errors: %lu\r\n", hp_uart.tx_errors);
printf("RX Errors: %lu\r\n", hp_uart.rx_errors);
// 计算吞吐量
static uint32_t last_time = 0;
static uint32_t last_tx = 0;
static uint32_t last_rx = 0;
uint32_t now = GetTick();
uint32_t elapsed = now - last_time;
if (elapsed >= 1000) {
uint32_t tx_rate = (hp_uart.tx_bytes - last_tx) * 1000 / elapsed;
uint32_t rx_rate = (hp_uart.rx_bytes - last_rx) * 1000 / elapsed;
printf("TX Rate: %lu bytes/s\r\n", tx_rate);
printf("RX Rate: %lu bytes/s\r\n", rx_rate);
last_time = now;
last_tx = hp_uart.tx_bytes;
last_rx = hp_uart.rx_bytes;
}
}
性能对比:
传统UART驱动:
- 吞吐量:~50KB/s
- CPU占用:~30%
- 延迟:~10ms
高性能UART驱动:
- 吞吐量:~200KB/s(提升4倍)
- CPU占用:~5%(降低83%)
- 延迟:~1ms(降低90%)
优化技术:
1. DMA传输
2. 零拷贝
3. 环形缓冲区
4. 批量处理
5. 中断优化
示例2:性能基准测试套件¶
创建一个完整的性能测试套件。
/**
* @brief 性能基准测试
*/
typedef struct {
const char *name;
void (*test_func)(void);
uint32_t iterations;
uint32_t total_cycles;
uint32_t avg_cycles;
} Benchmark_t;
#define MAX_BENCHMARKS 32
Benchmark_t benchmarks[MAX_BENCHMARKS];
uint8_t benchmark_count = 0;
/**
* @brief 注册基准测试
* @param name: 测试名称
* @param test_func: 测试函数
* @param iterations: 迭代次数
* @retval 无
*/
void Benchmark_Register(const char *name, void (*test_func)(void), uint32_t iterations) {
if (benchmark_count >= MAX_BENCHMARKS) {
return;
}
Benchmark_t *bench = &benchmarks[benchmark_count++];
bench->name = name;
bench->test_func = test_func;
bench->iterations = iterations;
bench->total_cycles = 0;
bench->avg_cycles = 0;
}
/**
* @brief 运行基准测试
* @param bench: 基准测试指针
* @retval 无
*/
void Benchmark_Run(Benchmark_t *bench) {
printf("Running: %s (%lu iterations)...\r\n", bench->name, bench->iterations);
uint32_t start = Profiler_GetCycles();
for (uint32_t i = 0; i < bench->iterations; i++) {
bench->test_func();
}
uint32_t end = Profiler_GetCycles();
bench->total_cycles = end - start;
bench->avg_cycles = bench->total_cycles / bench->iterations;
printf(" Total: %lu cycles (%.2f ms)\r\n",
bench->total_cycles, Profiler_GetTime_us(bench->total_cycles) / 1000.0f);
printf(" Avg: %lu cycles (%.2f us)\r\n",
bench->avg_cycles, Profiler_GetTime_us(bench->avg_cycles));
}
/**
* @brief 运行所有基准测试
* @param 无
* @retval 无
*/
void Benchmark_RunAll(void) {
printf("\r\n=== Performance Benchmarks ===\r\n");
for (uint8_t i = 0; i < benchmark_count; i++) {
Benchmark_Run(&benchmarks[i]);
}
printf("==============================\r\n");
}
/**
* @brief 基准测试:内存拷贝
*/
uint8_t bench_src[1024] __attribute__((aligned(4)));
uint8_t bench_dst[1024] __attribute__((aligned(4)));
void bench_memcpy(void) {
memcpy(bench_dst, bench_src, 1024);
}
void bench_dma_copy(void) {
DMA_MemCopy_Optimized(bench_src, bench_dst, 1024);
}
/**
* @brief 基准测试:数据处理
*/
uint32_t bench_data[256];
void bench_process_simple(void) {
for (int i = 0; i < 256; i++) {
bench_data[i] = bench_data[i] * 2 + 1;
}
}
void bench_process_optimized(void) {
// 使用SIMD或其他优化
uint32_t *ptr = bench_data;
for (int i = 0; i < 256; i += 4) {
ptr[0] = ptr[0] * 2 + 1;
ptr[1] = ptr[1] * 2 + 1;
ptr[2] = ptr[2] * 2 + 1;
ptr[3] = ptr[3] * 2 + 1;
ptr += 4;
}
}
/**
* @brief 基准测试:环形缓冲区
*/
RingBuffer_t bench_ring;
uint8_t bench_ring_buffer[256];
void bench_ringbuffer_write(void) {
uint8_t data[16] = {0};
RingBuffer_Write(&bench_ring, data, 16);
}
void bench_ringbuffer_read(void) {
uint8_t data[16];
RingBuffer_Read(&bench_ring, data, 16);
}
/**
* @brief 初始化基准测试
* @param 无
* @retval 无
*/
void Benchmark_Init(void) {
// 注册测试
Benchmark_Register("memcpy 1KB", bench_memcpy, 1000);
Benchmark_Register("DMA copy 1KB", bench_dma_copy, 1000);
Benchmark_Register("Process Simple", bench_process_simple, 1000);
Benchmark_Register("Process Optimized", bench_process_optimized, 1000);
Benchmark_Register("RingBuffer Write", bench_ringbuffer_write, 1000);
Benchmark_Register("RingBuffer Read", bench_ringbuffer_read, 1000);
// 初始化环形缓冲区
RingBuffer_Init(&bench_ring, bench_ring_buffer, sizeof(bench_ring_buffer));
}
示例3:实时性能监控¶
实现实时性能监控和报告。
/**
* @brief 性能监控器
*/
typedef struct {
uint32_t sample_interval; // 采样间隔(ms)
uint32_t last_sample_time; // 上次采样时间
// CPU统计
uint32_t cpu_usage; // CPU占用率
uint32_t cpu_peak; // CPU峰值
// 中断统计
uint32_t irq_count; // 中断次数
uint32_t irq_rate; // 中断频率
uint32_t last_irq_count; // 上次中断次数
// 内存统计
uint32_t mem_used; // 已用内存
uint32_t mem_peak; // 内存峰值
// DMA统计
uint32_t dma_transfers; // DMA传输次数
uint32_t dma_bytes; // DMA传输字节数
} PerfMonitor_t;
PerfMonitor_t perf_monitor;
/**
* @brief 初始化性能监控
* @param interval: 采样间隔(ms)
* @retval 无
*/
void PerfMonitor_Init(uint32_t interval) {
perf_monitor.sample_interval = interval;
perf_monitor.last_sample_time = GetTick();
perf_monitor.cpu_usage = 0;
perf_monitor.cpu_peak = 0;
perf_monitor.irq_count = 0;
perf_monitor.irq_rate = 0;
perf_monitor.last_irq_count = 0;
perf_monitor.mem_used = 0;
perf_monitor.mem_peak = 0;
perf_monitor.dma_transfers = 0;
perf_monitor.dma_bytes = 0;
}
/**
* @brief 更新性能监控(定期调用)
* @param 无
* @retval 无
*/
void PerfMonitor_Update(void) {
uint32_t now = GetTick();
if (now - perf_monitor.last_sample_time >= perf_monitor.sample_interval) {
// 更新CPU占用率
perf_monitor.cpu_usage = CPU_GetUsage();
if (perf_monitor.cpu_usage > perf_monitor.cpu_peak) {
perf_monitor.cpu_peak = perf_monitor.cpu_usage;
}
// 更新中断频率
uint32_t irq_delta = perf_monitor.irq_count - perf_monitor.last_irq_count;
uint32_t time_delta = now - perf_monitor.last_sample_time;
perf_monitor.irq_rate = irq_delta * 1000 / time_delta;
perf_monitor.last_irq_count = perf_monitor.irq_count;
perf_monitor.last_sample_time = now;
}
}
/**
* @brief 记录中断
* @param 无
* @retval 无
*/
void PerfMonitor_RecordIRQ(void) {
perf_monitor.irq_count++;
}
/**
* @brief 记录DMA传输
* @param bytes: 传输字节数
* @retval 无
*/
void PerfMonitor_RecordDMA(uint32_t bytes) {
perf_monitor.dma_transfers++;
perf_monitor.dma_bytes += bytes;
}
/**
* @brief 打印性能报告
* @param 无
* @retval 无
*/
void PerfMonitor_PrintReport(void) {
printf("\r\n=== Performance Report ===\r\n");
printf("CPU Usage: %lu%% (Peak: %lu%%)\r\n",
perf_monitor.cpu_usage, perf_monitor.cpu_peak);
printf("IRQ Rate: %lu/s (Total: %lu)\r\n",
perf_monitor.irq_rate, perf_monitor.irq_count);
printf("Memory: %lu bytes (Peak: %lu bytes)\r\n",
perf_monitor.mem_used, perf_monitor.mem_peak);
printf("DMA: %lu transfers, %lu bytes\r\n",
perf_monitor.dma_transfers, perf_monitor.dma_bytes);
printf("========================\r\n");
}
/**
* @brief 重置性能统计
* @param 无
* @retval 无
*/
void PerfMonitor_Reset(void) {
perf_monitor.cpu_peak = 0;
perf_monitor.irq_count = 0;
perf_monitor.last_irq_count = 0;
perf_monitor.mem_peak = 0;
perf_monitor.dma_transfers = 0;
perf_monitor.dma_bytes = 0;
}
深入理解¶
性能优化的权衡¶
性能优化往往需要在多个目标之间权衡。
常见权衡:
| 优化目标 | 优点 | 代价 | 适用场景 |
|---|---|---|---|
| 提高速度 | 响应快 | 功耗高、代码复杂 | 实时系统 |
| 降低功耗 | 省电 | 性能降低 | 电池供电 |
| 减少内存 | 成本低 | 性能降低 | 资源受限 |
| 提高可读性 | 易维护 | 性能可能降低 | 长期项目 |
优化决策树:
graph TD
A[性能问题] --> B{是否满足需求?}
B -->|是| C[不优化]
B -->|否| D{瓶颈在哪?}
D --> E[CPU]
D --> F[内存]
D --> G[I/O]
E --> H{可用DMA?}
H -->|是| I[使用DMA]
H -->|否| J[优化算法]
F --> K[减少拷贝]
G --> L[使用缓冲]
编译器优化¶
合理使用编译器优化选项可以显著提升性能。
GCC优化级别:
-O0: 无优化(调试用)
- 编译快
- 代码大
- 性能差
- 易调试
-O1: 基本优化
- 平衡编译时间和性能
- 适合开发阶段
-O2: 标准优化(推荐)
- 大多数优化
- 不增加代码大小
- 适合发布版本
-O3: 激进优化
- 所有优化
- 可能增加代码大小
- 适合性能关键代码
-Os: 优化大小
- 减小代码大小
- 适合Flash受限系统
-Ofast: 最快速度
- 可能违反标准
- 谨慎使用
优化属性:
// 函数内联
__attribute__((always_inline)) inline void fast_func(void) {
// 总是内联
}
// 不内联
__attribute__((noinline)) void no_inline_func(void) {
// 不内联
}
// 热点函数
__attribute__((hot)) void hot_func(void) {
// 编译器会更积极地优化
}
// 冷函数
__attribute__((cold)) void cold_func(void) {
// 优化代码大小而非速度
}
// 对齐
__attribute__((aligned(32))) uint8_t aligned_buffer[256];
// 打包
__attribute__((packed)) struct packed_struct {
uint8_t a;
uint32_t b;
};
Cache优化¶
理解和优化Cache行为可以大幅提升性能。
Cache基础:
STM32F4 Cache配置:
- I-Cache: 指令缓存,8KB
- D-Cache: 数据缓存,8KB
- Cache行大小:32字节
Cache命中:
- 数据在Cache中
- 访问速度快(1-2周期)
Cache缺失:
- 数据不在Cache中
- 需要从内存读取(数十周期)
Cache优化技巧:
/**
* @brief 数据对齐到Cache行
*/
#define CACHE_LINE_SIZE 32
// 对齐到Cache行
__attribute__((aligned(CACHE_LINE_SIZE)))
uint8_t cache_aligned_buffer[256];
/**
* @brief 避免False Sharing
*/
typedef struct {
volatile uint32_t counter1;
uint8_t padding1[CACHE_LINE_SIZE - sizeof(uint32_t)];
volatile uint32_t counter2;
uint8_t padding2[CACHE_LINE_SIZE - sizeof(uint32_t)];
} NoFalseSharing_t;
/**
* @brief 顺序访问优化
*/
void sequential_access_good(uint8_t *data, uint32_t size) {
// 好:顺序访问,Cache友好
for (uint32_t i = 0; i < size; i++) {
data[i] = i;
}
}
void random_access_bad(uint8_t *data, uint32_t size) {
// 差:随机访问,Cache不友好
for (uint32_t i = 0; i < size; i++) {
uint32_t index = (i * 7919) % size;
data[index] = i;
}
}
/**
* @brief 数据预取
*/
void prefetch_example(uint8_t *data, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
// 预取下一个Cache行
__builtin_prefetch(&data[i + CACHE_LINE_SIZE]);
// 处理当前数据
process_data(data[i]);
}
}
/**
* @brief 循环展开
*/
void loop_unroll_example(uint8_t *data, uint32_t size) {
uint32_t i;
// 展开4次
for (i = 0; i < size - 3; i += 4) {
data[i + 0] = process(data[i + 0]);
data[i + 1] = process(data[i + 1]);
data[i + 2] = process(data[i + 2]);
data[i + 3] = process(data[i + 3]);
}
// 处理剩余
for (; i < size; i++) {
data[i] = process(data[i]);
}
}
最佳实践¶
性能优化流程¶
1. 确定性能目标
- 吞吐量要求
- 延迟要求
- CPU占用限制
- 功耗限制
2. 建立基准
- 测量当前性能
- 记录基准数据
- 识别瓶颈
3. 分析瓶颈
- 使用profiling工具
- 找出热点代码
- 分析原因
4. 制定优化方案
- 选择优化技术
- 评估收益和成本
- 制定实施计划
5. 实施优化
- 逐步优化
- 保持代码可读性
- 添加注释说明
6. 测量效果
- 对比优化前后
- 验证功能正确性
- 检查副作用
7. 迭代优化
- 继续优化瓶颈
- 直到达到目标
- 或收益递减
常见陷阱¶
1. 过早优化
- 在功能完成前优化
- 优化不重要的代码
- 牺牲可读性
2. 盲目优化
- 不测量就优化
- 凭感觉优化
- 优化错误的地方
3. 过度优化
- 微优化
- 牺牲可维护性
- 增加复杂度
4. 忽略副作用
- 功耗增加
- 内存占用增加
- 代码可读性降低
5. 不验证结果
- 不测量优化效果
- 不验证功能正确性
- 引入新bug
优化检查清单¶
□ 是否测量了基准性能?
□ 是否识别了性能瓶颈?
□ 是否选择了合适的优化技术?
□ 是否保持了代码可读性?
□ 是否添加了必要的注释?
□ 是否测量了优化效果?
□ 是否验证了功能正确性?
□ 是否检查了副作用?
□ 是否达到了性能目标?
□ 是否值得继续优化?
总结¶
驱动性能优化是一个系统工程,需要:
- 科学的方法:先测量,后优化,再验证
- 合适的工具:profiling工具、性能计数器、逻辑分析仪
- 多种技术:中断优化、DMA、零拷贝、缓存优化
- 权衡取舍:性能、功耗、内存、可读性
- 持续改进:迭代优化,不断提升
通过本教程的学习,你应该掌握了:
- 性能分析和测量方法
- 中断处理优化技术
- DMA传输优化策略
- 缓存和缓冲区优化
- 零拷贝技术应用
- CPU占用优化方法
- 编译器和Cache优化
记住:优化是手段,不是目的。只有在需要时才优化,优化要有度。
扩展阅读¶
- 《深入理解计算机系统》- Cache和内存层次
- 《嵌入式系统设计》- 实时性能优化
- ARM Cortex-M4技术参考手册
- STM32F4 DMA应用笔记
- 《编写高效的C代码》
练习题¶
- 使用DWT实现一个微秒级精度的性能计数器
- 优化一个UART驱动,使吞吐量提升至少50%
- 实现一个零拷贝的网络数据包处理系统
- 分析并优化一个高频中断的处理延迟
- 设计一个自适应的缓冲区管理策略
注意:性能优化需要根据具体应用场景调整,本教程提供的是通用方法和技术,实际应用时需要结合具体需求进行优化。