c++atomicmemory-barriersstdatomiccompare-and-swap

Is it necessary to call load() before compare_exchange_strong()?


I was learning about C++ memory sequence, and I found this code in Unreal Engine5. My question is why not call compare_exchange_strong() directly instead of load() first?

FHttpRequest* GetFreeRequest()
{
    for (uint8 i = 0; i < Pool.Num(); ++i)
    {
    if (!Pool[i].Usage.load(std::memory_order_relaxed)) //<-why load first?
    {
        uint8 Expected = 0u;
        if (Pool[i].Usage.compare_exchange_strong(Expected, 1u))
        {
            Pool[i].Request->Reset();
            return Pool[i].Request;
        }
    }
    }
    return nullptr;
}

I think it's okay to remove the load() call and change it to something like this:

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;
}

Solution

  • why load first?

    Because .load(std::memory_order_relaxed) (read) is fast and .compare_exchange_strong(Expected, 1u) (read-modify-write) is slow.

    This is slow once, and then fast until Usage is reset.

    if (!Pool[i].Usage.load(std::memory_order_relaxed)) {
        // ...
        if (Pool[i].Usage.compare_exchange_strong(Expected, 1u))
    

    This is slow always

    if (Pool[i].Usage.compare_exchange_strong(Expected, 1u))