You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在真实编译器与CPU上复现relaxed内存序下的LoadStore重排?

#include <atomic>
#include <thread>

void test_relaxed()
{
    using namespace std;
    atomic<int> x{0};
    atomic<int> y{0};

    std::thread t1([&] {
        auto r1 = y.load(memory_order_relaxed); //a
        x.store(r1, memory_order_relaxed); //b
        });

    std::thread t2([&] {
        auto r2 = x.load(memory_order_relaxed); //c
        y.store(42, memory_order_relaxed); //d
    });
    
    t1.join();
    t2.join();
}

根据C++参考文档的relaxed ordering示例,上述代码允许出现r1 == r2 == 42的结果,但在x86-64和arm64平台测试后始终无法复现该现象,如何在真实编译器与CPU环境中重现这一结果?


复现r1 == r2 == 42的具体方法

要触发这个结果,核心是让指令执行顺序刚好满足d → a → b → c(线程2先执行y.store(42),线程1加载到y的42并存入x,最后线程2加载x的42),以下是可行手段:

  1. 大循环迭代测试
    单次测试出现目标结果的概率极低,必须将测试逻辑包裹在百万级以上的循环中,统计触发次数:
#include <atomic>
#include <thread>
#include <iostream>

int main() {
    int hit_count = 0;
    const int total_iter = 1000000;
    for (int i = 0; i < total_iter; ++i) {
        std::atomic<int> x{0};
        std::atomic<int> y{0};
        int r1 = 0, r2 = 0;

        std::thread t1([&] {
            r1 = y.load(std::memory_order_relaxed);
            x.store(r1, std::memory_order_relaxed);
        });

        std::thread t2([&] {
            r2 = x.load(std::memory_order_relaxed);
            y.store(42, std::memory_order_relaxed);
        });

        t1.join();
        t2.join();

        if (r1 == 42 && r2 == 42) {
            hit_count++;
        }
    }
    std::cout << "触发r1==r2==42的次数: " << hit_count << "/" << total_iter << "\n";
    return 0;
}
  1. 插入指令延迟干扰
    在关键内存操作前后插入空操作,强制CPU调度产生延迟,增加内存可见性延迟或指令重排的概率:
// 线程1(GCC/clang语法)
std::thread t1([&] {
    // 插入空操作延迟,让线程2优先执行store
    for (int k = 0; k < 100; ++k) { __asm__ volatile("nop"); }
    r1 = y.load(std::memory_order_relaxed);
    x.store(r1, std::memory_order_relaxed);
});

// 线程2
std::thread t2([&] {
    r2 = x.load(std::memory_order_relaxed);
    // 延迟让线程1的load看到store结果
    for (int k = 0; k < 100; ++k) { __asm__ volatile("nop"); }
    y.store(42, std::memory_order_relaxed);
});

MSVC可替换为__nop()指令。

  1. 绑定线程到不同CPU核心
    将两个线程绑定到不同物理核心,利用跨核内存可见性的延迟提升触发概率(Linux示例):
#include <pthread.h>
void bind_to_cpu(int cpu_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(cpu_id, &cpuset);
    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}

// 线程函数开头添加绑定逻辑
std::thread t1([&] {
    bind_to_cpu(0);
    r1 = y.load(std::memory_order_relaxed);
    x.store(r1, std::memory_order_relaxed);
});

std::thread t2([&] {
    bind_to_cpu(1);
    r2 = x.load(std::memory_order_relaxed);
    y.store(42, std::memory_order_relaxed);
});
  1. 切换到弱内存模型平台
    x86-64是TSO(全存储顺序)内存模型,对store-load操作有默认顺序保证;arm64的内存模型也比ARMv7更强。优先选择ARMv7或更早的ARM架构平台测试,这类弱内存模型更容易触发relaxed序的极端情况。

为什么x86/arm64难复现?

x86-64的TSO模型保证所有store操作按全局顺序执行,线程的store对其他线程的load有一定可见性顺序;arm64虽然支持relaxed内存序,但相比旧ARM架构增强了内存一致性,所以这类极端指令重排的出现概率极低,必须通过大量迭代或人为干扰才能触发。

内容的提问来源于stack exchange,提问作者Jichao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 02:58:00