假设我们需要将一个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语言特性(指针转换、位操作),编写出比标准库函数(在某些非优化编译器上)或朴素实现性能更高的代码。
|