ARM GCC:无需volatile实现存储-存储排序的高效方案
ARM Cortex-M4 环形缓冲区共享索引的高效无volatile实现
我需要用共享索引标记共享环形缓冲区的数据写入完成,在ARM Cortex-M4平台(使用arm gcc 9.3.1,编译选项-O3)上,想找一种高效实现方式,同时避免使用不推荐的volatile关键字。
测试代码与平台差异
先看两个测试函数:
void Test1(int volatile* x) { *x = 5; } void Test2(int* x) { __atomic_store_n(x, 5, __ATOMIC_RELEASE); }
在x86平台上,两者编译后的汇编完全一致,没有额外开销:
0000000000000000 <Test1>: 0: c7 07 05 00 00 00 movl $0x5,(%rdi) 6: c3 retq 7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) e: 00 00 0000000000000010 <Test2>: 10: c7 07 05 00 00 00 movl $0x5,(%rdi) 16: c3 retq
但在ARM Cortex-M4上,差异明显:
volatile版本仅生成简单的存储指令,无内存屏障:
00000000 <Test1>: 0: 2305 movs r3, #5 2: 6003 str r3, [r0, #0] 4: 4770 bx lr 6: bf00 nop
__atomic_store_n带__ATOMIC_RELEASE内存序会生成dmb ish数据内存屏障,带来额外开销:
00000000 <Test2>: 0: 2305 movs r3, #5 2: f3bf 8f5b dmb ish 6: 6003 str r3, [r0, #0] 8: 4770 bx lr a: bf00 nop
解决方案:使用__ATOMIC_RELAXED内存序
要同时避免volatile和不必要的内存屏障,可以使用__atomic_store_n并指定__ATOMIC_RELAXED内存序:
void Test3(int* x) { __atomic_store_n(x, 5, __ATOMIC_RELAXED); }
该代码在ARM Cortex-M4上编译后的汇编和volatile版本完全一致,无内存屏障:
00000000 <Test3>: 0: 2305 movs r3, #5 2: 6003 str r3, [r0, #0] 4: 4770 bx lr 6: bf00 nop
关键注意事项
- 读取共享索引时,必须使用对应的原子读取操作
__atomic_load_n(x, __ATOMIC_RELAXED),确保编译器不会优化读取操作,同时保证硬件层面的可见性。 __ATOMIC_RELAXED适用于仅需要保证变量读写不被编译器优化、且硬件能保证可见性的场景。对于Cortex-M4单处理器系统(如中断与线程交互的环形缓冲区),这种内存序完全满足需求;如果是多处理器系统,需根据实际同步需求选择更强的内存序。
内容的提问来源于stack exchange,提问作者personal_cloud
相关产品推荐
相关产品推荐

