调用compare_exchange_strong前是否需先调用load?UE5代码疑问
Unreal Engine5中原子操作先load再compare_exchange_strong的原因解析
我在学习C++内存序时,看到Unreal Engine5里这段获取空闲请求的代码,疑惑为什么要先调用load()再执行compare_exchange_strong(),甚至想直接移除load()调用,下面来拆解这个问题。
原代码
FHttpRequest* GetFreeRequest() { for (uint8 i = 0; i < Pool.Num(); ++i) { if (!Pool[i].Usage.load(std::memory_order_relaxed)) // <- 为什么先load? { uint8 Expected = 0u; if (Pool[i].Usage.compare_exchange_strong(Expected, 1u)) { Pool[i].Request->Reset(); return Pool[i].Request; } } } return nullptr; }
你提出的简化版本
FHttpRequest* GetFreeRequest() { for (uint8 i = 0; i < Pool.Num(); ++i) { uint8 Expected = 0u; if (Pool[i].Usage.compare_exchange_strong(Expected, 1u)) { Pool[i].Request->Reset(); return Pool[i].Request; } } return nullptr; }
为什么原代码要先调用load()?
核心原因是性能优化:
load(std::memory_order_relaxed)是开销极低的原子操作,几乎和普通内存读取一样快,不需要任何内存同步屏障。compare_exchange_strong是相对重的原子指令,它需要完成“读取当前值→比较→更新值”的原子操作,在多线程竞争场景下还可能引发缓存一致性协议的额外开销。- 先通过
load做一次预筛选:如果Usage已经是1(被占用),直接跳过后续的compare_exchange_strong,避免对明确被占用的元素执行昂贵的原子交换操作。在请求池大部分元素被占用的场景下,这种预筛选能大幅减少不必要的原子指令执行次数,提升整体性能。
另外,这里用memory_order_relaxed是安全的:即使load读到了过时的值(比如其他线程刚把Usage从0改成1,但relaxed内存序没同步这个变化),后续的compare_exchange_strong会做最终的原子验证,不会错误地抢占已被占用的请求——最多只是多执行一次compare_exchange_strong,但这种情况概率很低,整体性能收益远大于这点额外开销。
你的简化版本可行吗?
功能上完全可行,因为compare_exchange_strong本身就会先读取原子变量的当前值和Expected比较:如果相等就更新为1,返回true;如果不等就把当前值写入Expected,返回false。逻辑上和原代码是等价的,不会出现错误。
但性能上不如原代码:当请求池里大部分元素都被占用时,你的版本会对每个元素都执行一次compare_exchange_strong,而原代码只会对load判断为0的元素执行这个重操作,前者的原子指令开销会显著更高。
内容的提问来源于stack exchange,提问作者Fosky etm
相关产品推荐
相关产品推荐

