x86平台GCC是否提供可高效复制volatile数组的内置类memcpy函数
我正在结合C语言的volatile关键字与x86内存序保证(写操作间保序、读操作间保序)实现无屏障消息队列,请问GCC是否提供了可在两个volatile数组间高效拷贝数据的内置函数?
也就是说,是否存在可达到如下示例中memcpy_volatile调用效果的内置/高效函数,无需手写朴素循环实现?
uint8_t volatile * dest = ...; uint8_t volatile const* src = ...; int len; memcpy_volatile(dest, src, len);
前置说明:本问题不讨论无屏障C程序的合理性,我完全知晓基于内存屏障的替代实现方案,因此不需要“使用屏障原语”类的回答,本问题也并非未限定x86/GCC平台、答案为“不存在跨平台通用机制”的同类问题的重复。
补充说明
不要求memcpy_volatile具备原子性,其内部操作的执行顺序无关紧要。核心要求为:若memcpy_volatile(dest, ...)执行完成后,才通过另一个volatile变量将dest指针通告给其他线程,那么(数据写入、指针写入)的操作序列必须对其他线程呈现一致顺序——即其他线程若观测到新指针dest,就必然能观测到已拷贝至*dest的数据,这是无屏障队列实现的核心要求。
GCC没有单独推出命名为memcpy_volatile的专属内置函数,但可以直接通过现有内置函数实现完全符合要求的高性能拷贝,性能和普通非volatile内存的memcpy基本持平,不需要手写低效的逐字节循环。
具体实现方式是直接调用GCC内置的__builtin_memcpy,传入带may_alias属性的volatile指针即可,参考写法如下:
static inline void memcpy_volatile(volatile void *dest, volatile const void *src, size_t len) { __builtin_memcpy( (volatile void __attribute__((may_alias)) *)dest, (volatile const void __attribute__((may_alias)) *)src, len ); }
这个实现的特性完全匹配需求:
- 性能表现和普通
memcpy一致:GCC不会把这段代码编译成逐字节循环,会根据拷贝长度自动选择最优指令序列:短拷贝用通用寄存器直接mov,中等长度用x86字符串操作指令,长拷贝自动生成SSE/AVX向量批量拷贝指令,没有额外性能损耗。 - 顺序保证符合要求:GCC对volatile访问的实现规则是不会将volatile内存访问跨其他volatile操作重排,
__builtin_memcpy生成的所有对目标缓冲区的写入都会被识别为volatile访问,全部排在函数返回前执行,不会被调度到后续对volatile通告指针的写入之后。配合x86硬件自带的写-写保序特性,其他线程一旦观测到更新后的dest指针,必然能观测到全部拷贝完成的数据。 - 拷贝内部的操作顺序不做强制约束:GCC可以自由调整拷贝内部的读写调度、向量化拆分,完全符合“内部执行顺序无关紧要”的要求,不会因为volatile限定损失拷贝性能。
注意不要直接调用C标准库的memcpy:标准库memcpy的形参不带volatile限定,编译器可能假设其内部不涉及volatile内存访问,违规重排操作顺序。必须使用GCC内置的__builtin_memcpy,在GCC 8及以上版本的x86环境下行为完全符合预期,更低版本也可以通过相同写法获得正确的顺序保证,只是部分老版本在长拷贝场景下的指令选择略差,性能差距在可接受范围内。
内容的提问来源于stack exchange,提问作者personal_cloud

