标签: memory-barriers

ARM Cortex M 中的 ISB 指令

到目前为止,我使用了 3 个 NOP 来“清理”管道。最近我遇到了 ISB 指令,它可以帮我做到这一点。查看arm信息中心,我注意到这个命令需要4个周期(在Cortex M0下),而3个NOP只需要3个周期。

我为什么要使用这个命令?它与 3 NOP 有什么不同?

assembly arm instructions memory-barriers cortex-m

2
推荐指数
1
解决办法
2734
查看次数

内存屏障是否既充当标记又充当指令?

我读过有关内存屏障如何工作的不同内容。

例如,用户Johan在这个问题中的回答说,内存屏障是 CPU 执行的指令。

虽然用户Peter Cordes在这个问题中的评论说了以下关于 CPU 如何重新排序指令的内容:

它的读取速度比执行速度快,因此它可以看到即将到来的指令的窗口。有关详细信息,请参阅 x86 标签 wiki 中的一些链接,例如 Agner Fog 的 microarch pdf,以及 David Kanter 对 Intel Haswell 设计的文章。当然,如果您只是用谷歌搜索“乱序执行”,您会找到您应该阅读的维基百科文章。

所以我根据上面的评论猜测,如果指令之间存在内存屏障,CPU将看到这个内存屏障,这导致CPU不会对指令重新排序,所以这意味着内存屏障是一个“标记”让CPU看到而不是执行。


现在我的猜测是,内存屏障既充当标记又充当 CPU 执行的指令。

对于标记部分,CPU 看到指令之间存在内存屏障,这导致 CPU 不会对指令进行重新排序。

至于指令部分,CPU会执行内存屏障指令,这会导致CPU做一些诸如刷新存储缓冲区之类的事情,然后CPU会继续执行内存屏障之后的指令。

我对么?

x86 assembly instruction-set cpu-architecture memory-barriers

2
推荐指数
1
解决办法
625
查看次数

如果两个原子的 fetch_add 同时执行会发生什么?

据我所知,cpp11中原子类型的原子操作保证是aomtic。但是,假设在多核系统中,如果两个线程同时进行以下操作,结果会是1吗?(假设最初atomic<int> val=0;)看起来结果肯定是2,但为什么呢?

val.fetch_add(1,std::memory_order_relaxed);

作为补充,假设另一种情况,如果 thread1 do val.load(2);thread2 do val.load(3),看起来结果是 2 还是 3,但也不确定是哪一个。

concurrency atomic memory-model memory-barriers c++11

2
推荐指数
1
解决办法
601
查看次数

rdtsc乱序执行的解决方案?

我正在尝试将clock_gettime(CLOCK_REALTIME, &ts) 替换为rdtsc,以CPU 周期而不是服务器时间来衡量代码执行时间。基准测试代码的执行时间对于软件至关重要。我尝试在独立核心上的 x86_64 3.20GHz ubuntu 机器上运行代码并得到以下数字:

情况 1:时钟获取时间: 24 纳秒

void gettime(Timespec &ts) {
        clock_gettime(CLOCK_REALTIME, &ts);
}
Run Code Online (Sandbox Code Playgroud)

情况 2:rdtsc(没有 mfence 和编译器屏障): 10 ns

void rdtsc(uint64_t& tsc) {
        unsigned int lo,hi;
        __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
        tsc = ((uint64_t)hi << 32) | lo;
}
Run Code Online (Sandbox Code Playgroud)

情况 3:rdtsc(带有 mfence 和编译器屏障): 30 ns

void rdtsc(uint64_t& tsc) {
        unsigned int lo,hi;
        __asm__ __volatile__ ("mfence;rdtsc" : "=a" (lo), "=d" (hi) :: "memory");
        tsc = ((uint64_t)hi << 32) | lo;
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是我知道 …

c++ gcc cpu-architecture memory-barriers rdtsc

2
推荐指数
1
解决办法
1298
查看次数

C11 独立内存屏障 LoadLoad StoreStore LoadStore StoreLoad

我想在原子和非原子操作之间使用独立的内存屏障(我认为无论如何它都不重要)。我想我了解存储屏障和加载屏障的含义以及 4 种可能的内存重新排序;LoadLoad, StoreStore, LoadStore, StoreLoad.

但是,我总是发现获取/释放概念令人困惑。因为在阅读文档时,acquire 不仅说到loads,还说到stores,而release 不仅说到stores,还说到loads。另一方面,普通负载障碍仅为您提供负载保证,而普通商店障碍仅为您提供商店保证。

我的问题如下。在 C11/C++11 中,将独立atomic_thread_fence(memory_order_acquire)视为负载屏障(防止LoadLoad重新排序)和atomic_thread_fence(memory_order_release)存储屏障(防止StoreStore重新排序)是否安全?

如果以上是正确的,我可以用什么来防止LoadStoreStoreLoad重新排序?

当然,我对可移植性感兴趣,我不在乎上述在特定平台上产生什么。

c c++ memory-model memory-barriers stdatomic

2
推荐指数
1
解决办法
368
查看次数

使用 memory_order_seq_cst 和 memory_order_release 可能的排序

参考下面的代码

auto x = std::atomic<std::uint64_t>{0};
auto y = std::atomic<std::uint64_t>{0};

// thread 1
x.store(1, std::memory_order_release);
auto one = y.load(std::memory_order_seq_cst);

// thread 2
y.fetch_add(1, std::memory_order_seq_cst);
auto two = x.load(std::memory_order_seq_cst);
Run Code Online (Sandbox Code Playgroud)

这里有可能onetwo都为 0 吗?


(我似乎遇到了一个错误,在上面的代码运行后,如果onetwo都可以保持 0 的值,则可以解释该错误。并且排序规则太复杂,我无法弄清楚上面可以进行哪些排序。)

c++ multithreading memory-barriers stdatomic c++20

2
推荐指数
1
解决办法
719
查看次数

程序计数器、栅栏和处理器重新排序

我知道除了编译器之外,处理器还可以重新排序指令。

我有几个问题想不通。

假设我们有三个指令:

节目单

S1 S2 S3

处理器重新排序后,顺序变为(无论出于何种原因):

S3 S2 S1

  • 那么当处理器执行 S1(按程序顺序)时,程序计数器的值是多少?
  • 如果 Windows(或其他操作系统),上下文将线程切换出并在另一个处理器中调度它,另一个处理器如何知道接下来要执行哪条指令?(是否保证进行相同的重新排序?)
  • 一个处理器上的内存栅栏(例如,由原子比较和交换指令创建的完整栅栏)在线程被调度到另一个线程上之后是否有效?

对此的任何想法都受到高度赞赏。

windows assembly x86-64 cpu-architecture memory-barriers

2
推荐指数
1
解决办法
60
查看次数

多个读取器可以通过获取/释放顺序与同一写入器同步吗?

读完“行动中的并发”后,我找不到一个问题的答案 - 当我们在一个原子变量上有一个存储(释放)和许多加载(获取)时,读取副作用的标准是否有保证?假设我们有:

int i{};
atomic<bool> b{};

void writer(){
 i=42;
 b.store(true,memory_order_release);
}

void reader(){
 while(!b.load(memory_order_acquire))
    this_thread::yield();
 assert(i==42);
}
//---------------------
thread t1{writer},t2{reader},t3{reader};
Run Code Online (Sandbox Code Playgroud)

如果我们只有一个读取器,一切都可以,但是我们是否可以在 t2 或 t3 线程中断言失败?

c++ atomic memory-model memory-barriers stdatomic

2
推荐指数
1
解决办法
223
查看次数

Windows 上*单 CPU*(多核)上的 C++ 多线程是否存在“缓存一致性”问题?

(编辑:只是为了澄清:“缓存一致性”的问题是在不使用原子变量的情况下。)

是否有可能(单CPU情况:Windows可以在Intel / AMD / Arm CPU上运行),线程1运行在core-1上存储一个bool变量(例如)并且它保留在L1缓存中,而线程2在 core-n 上运行使用该变量,并且它会查找内存中该变量的另一个副本?

代码示例(为了演示该问题,我们假设这std::atomic_bool只是一个普通的bool):

#include <thread>
#include <atomic>
#include <chrono>

std::atomic_bool g_exit{ false }, g_exited{ false };

using namespace std::chrono_literals;

void fn()
{
    while (!g_exit.load(std::memory_order_acquire))
    {
        // do something (lets say it takes 1-4s, repeatedly)
        std::this_thread::sleep_for(1s);
    }

    g_exited.store(true, std::memory_order_release);
}

int main()
{
    std::thread wt(fn);
    wt.detach();

    // do something (lets say it took 2s)
    std::this_thread::sleep_for(2s);

    // Exit

    g_exit.store(true, std::memory_order_release);

    for (int i = 0; i < 5; …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading cpu-architecture memory-barriers stdatomic

2
推荐指数
1
解决办法
1079
查看次数

互斥锁定和解锁功能如何防止CPU重新排序?

据我所知,函数调用充当编译器障碍,但不作为CPU障碍.

教程说明如下:

获取锁意味着获取语义,而释放锁意味着释放语义!其间的所有内存操作都包含在一个漂亮的小屏障三明治中,防止任何不希望的内存重新排序跨越边界.

我假设上面的引用是关于CPU重新排序而不是编译器重新排序.

但我不明白互斥锁和解锁如何导致CPU赋予这些函数获取和释放语义.

例如,如果我们有以下C代码:

pthread_mutex_lock(&lock);
i = 10;
j = 20;
pthread_mutex_unlock(&lock);
Run Code Online (Sandbox Code Playgroud)

上面的C代码被翻译成以下(伪)汇编指令:

push the address of lock into the stack
call pthread_mutex_lock()
mov 10 into i
mov 20 into j
push the address of lock into the stack
call pthread_mutex_unlock()
Run Code Online (Sandbox Code Playgroud)

现在是什么阻止了CPU重新排序mov 10 into i以及mov 20 into j 上方call pthread_mutex_lock()或下方call pthread_mutex_unlock()

如果它是call阻止CPU进行重新排序的指令,那么为什么我引用的教程使它看起来像是互斥锁和解锁函数来阻止CPU重新排序,为什么我引用的教程没有说任何函数调用会阻止CPU重新排序吗?

我的问题是关于x86架构.

c x86 assembly mutex memory-barriers

1
推荐指数
2
解决办法
549
查看次数