假设我们需要将一个int类型的数组清零。 版本 1:朴素的循环实现 (未优化)这是最直观的写法,初学者通常会这样写: #include <stdint.h>
#define ARRAY_SIZE 128
uint32_t data_array[ARRAY_SIZE];
void clear_array_naive() {
for (int i = 0; i < ARRAY_SIZE; i++) {
data_array[i] = 0;
}
}
这段代码逐个元素地将数组清零。在大多数编译器中,它会被优化,但我们仍有手动优化的空间,特别是在一些优化能力较弱的嵌入式编译器上。 版本 2:结合指针和使用指针代替数组下标访问,可以减少每次循环中计算地址的开销。 #include <stdint.h>
#define ARRAY_SIZE 128
uint32_t data_array[ARRAY_SIZE];
void clear_array_pointer() {
uint32_t *ptr = data_array;
// 使用 sizeof 计算迭代次数,更具通用性
int count = sizeof(data_array) / sizeof(data_array[0]);
for (int i = 0; i < count; i++) {
*ptr++ = 0;
}
}
*ptr++ = 0; 这行代码做了两件事:1. 将ptr指向的地址写入0;2. 将ptr指针移动到下一个元素。这通常比data_array更高效。 版本 3:终极优化 - 利用CPU字长进行“块”操作这是精髓所在。现代CPU(如ARM Cortex-M系列)通常是32位或64位的。这意味着CPU在一个时钟周期内可以处理32位(4字节)或64位(8字节)的数据。如果我们仍然以char(1字节)或int(4字节)为单位进行操作,就没有充分利用CPU的“带宽”。 这个优化思路是:将内存操作的单位从“字节”提升到CPU的“字长”。 假设我们运行在一个32位CPU上,并且我们想清零一个大块内存。我们可以将指向内存的指针强制转换为指向uint32_t(32位无符号整数)的指针,然后以4字节为单位进行写入。 #include <stdint.h>
#include <stddef.h> // For size_t
// 假设我们有一个更大的、字节为单位的缓冲区
#define BUFFER_SIZE 1024
uint8_t buffer[BUFFER_SIZE];
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 使用字长对齐的方式快速清零一块内存区域
* @param p_dest 目标内存区域的指针
* @param size 要清零的字节数
*/
void fast_memory_clear(void *p_dest, size_t size) {
// 1. 将通用指针转换为我们可以进行算术运算的指针
uint8_t *dest = (uint8_t *)p_dest;
size_t count = size;
// 2. 先按字节处理,直到地址对齐到4字节(32位)边界
// `((uintptr_t)dest & 3)` 这是一个位操作技巧,用于检查地址的低2位。
// 如果地址是4的倍数,低2位必然是00。
while (count > 0 && ((uintptr_t)dest & 3) != 0) {
*dest++ = 0;
count--;
}
// 3. 按“字”(4字节)为单位进行快速清零
uint32_t *dest_word = (uint32_t *)dest;
size_t word_count = count / 4;
for (size_t i = 0; i < word_count; i++) {
*dest_word++ = 0;
}
// 4. 处理剩余的不足一个“字”的字节
dest = (uint8_t *)dest_word;
count %= 4;
while (count > 0) {
*dest++ = 0;
count--;
}
}
// 使用示例
void run_example() {
fast_memory_clear(buffer, BUFFER_SIZE);
}
优化效果与原理解释这段 fast_memory_clear 函数的优化效果体现在以下几个方面: 减少循环次数,提升CPU效率: 核心思想:对于一个1024字节的缓冲区,朴素的memset或逐字节清零需要执行1024次循环和1024次写操作。 优化后:在地址对齐后,fast_memory_clear 将大部分工作交给了以uint32_t为单位的循环。它只需要执行 1024 / 4 = 256 次循环。循环次数减少了75%。 效果:由于循环本身(如 i++、条件判断、跳转)也需要CPU指令,大幅减少循环次数能显著降低这部分开销。更重要的是,每次写操作都利用了CPU的32位数据总线,在同样的时间内完成了4倍的工作量。
利用位操作技巧实现高效对齐检查: 代码的健壮性 (Robustness):
结论这段代码是嵌入式C语言中一个典型的优化范例。它展示了如何通过深入理解底层硬件(CPU字长、指令效率)和巧妙运用C语言特性(指针转换、位操作),编写出比标准库函数(在某些非优化编译器上)或朴素实现性能更高的代码。
|