为何编译器不对RAM查找做优化?附C++代码与汇编分析
C++循环内存加载优化问题分析
C++源代码
#include <stdint.h> #include <cstdlib> void Send(uint32_t); void SendBuffer(uint32_t* __restrict__ buff, size_t n) { for (size_t i = 0; i < n; ++i) { Send(buff[0]); Send(buff[1]); for (size_t j = 0; j < i; ++j) { Send(buff[j]); } } }
对应汇编代码
SendBuffer(unsigned int*, unsigned long): test rsi, rsi je .L15 push r13 mov r13, rsi push r12 mov r12, rdi push rbp xor ebp, ebp push rbx sub rsp, 8 .L5: mov edi, DWORD PTR [r12] call Send(unsigned int) mov edi, DWORD PTR [r12+4] call Send(unsigned int) test rbp, rbp je .L3 xor ebx, ebx .L4: mov edi, DWORD PTR [r12+rbx*4] add rbx, 1 call Send(unsigned int) cmp rbx, rbp jne .L4 .L3: add rbp, 1 cmp r13, rbp jne .L5 add rsp, 8 pop rbx pop rbp pop r12 pop r13 ret .L15: ret
观察到每次循环迭代都会重复从内存读取值,而这些值本可提前存入寄存器复用,以下是针对相关技术问题的解答:
1. 无论是否使用__restrict__关键字,按C++标准,编译器是否允许将内存值提前加载到寄存器?
允许,但有严格前提:编译器必须能证明内存中的值在后续读取前不会被修改。
- 不使用
__restrict__时:编译器需要排除Send函数修改buff指向内存的可能性,但Send是仅声明未定义的外部函数,编译器默认无法确定它是否会通过全局指针、别名等方式篡改buff的内容,因此不能随意缓存值到寄存器。 - 使用
__restrict__时:该关键字仅保证buff是访问其指向内存的唯一指针,但不保证Send不会修改该内存(比如Send可能通过全局变量拿到同一块内存的地址)。标准层面仍允许优化,但编译器需要额外证据证明内存不会被Send修改。
2. 若该操作合规,为何编译器不执行此优化?
核心原因是编译器无法确认Send函数不会修改buff指向的内存:
Send作为未定义的外部函数,编译器默认假设它可能产生副作用(比如修改全局状态、通过别名访问buff的内存)。如果提前把buff[0]、buff[1]加载到寄存器,而Send实际修改了这些内存,会导致后续读取的值和内存实际值不一致,违反程序语义。- 内层循环的存在进一步放大了不确定性:内层循环多次调用
Send,每次调用都可能修改内存,编译器无法安全假设buff的内容在整个外层循环迭代中保持不变。
即使加了__restrict__,它只解决了指针别名问题,没解决外部函数修改内存的问题,所以编译器不敢执行此优化。
3. 如何告知编译器该内存不会被修改以启用此优化?
可以通过以下方式明确传递内存只读的信息:
- 标记
Send为纯函数:如果Send不会修改任何外部内存,用[[gnu::pure]](GCC/Clang)或[[msvc::pure]](MSVC)属性标记它,告诉编译器该函数无副作用,不会修改外部状态。示例:void Send(uint32_t) [[gnu::pure]]; - 手动缓存值到局部变量:显式在代码中把
buff[0]、buff[1]提前读取到局部变量,编译器通常会将局部变量分配到寄存器:void SendBuffer(uint32_t* __restrict__ buff, size_t n) { if (n == 0) return; uint32_t val0 = buff[0]; uint32_t val1 = buff[1]; for (size_t i = 0; i < n; ++i) { Send(val0); Send(val1); for (size_t j = 0; j < i; ++j) { Send(buff[j]); } } } - 添加
const限定指针:如果buff指向的内存确实是只读的,将指针声明为const uint32_t* __restrict__,结合Send的纯函数属性,编译器会更有信心执行优化。
内容的提问来源于stack exchange,提问作者pvl
相关产品推荐
相关产品推荐

