相关疑难解决方法(0)

为什么这个“std::atomic_thread_fence”起作用

首先我想谈一下我对此的一些理解,如有错误请指正。

  1. aMFENCE在x86中可以保证全屏障
  2. 顺序一致性可防止 STORE-STORE、STORE-LOAD、LOAD-STORE 和 LOAD-LOAD 重新排序

    这是根据维基百科的说法。

  3. std::memory_order_seq_cst不保证防止 STORE-LOAD 重新排序。

    这是根据Alex 的回答,“负载可能会通过早期存储重新排序到不同位置”(对于 x86),并且 mfence 不会总是被添加。

    a是否std::memory_order_seq_cst表示顺序一致性?根据第2/3点,我认为这似乎不正确。std::memory_order_seq_cst仅当以下情况时才表示顺序一致性

    1. 至少一个显式MFENCE添加到任一LOADSTORE
    2. LOAD(无栅栏)和 LOCK XCHG
    3. LOCK XADD ( 0 ) 和 STORE (无栅栏)

    否则仍有可能重新订购。

    根据@LWimsey的评论,我在这里犯了一个错误,如果 和LOAD都是STOREmemory_order_seq_cst则没有重新排序。Alex 可能指出使用非原子或非 SC 的情况。

  4. std::atomic_thread_fence(memory_order_seq_cst)总是产生一个完整的屏障

    这是根据Alex的回答。所以我总是可以替换asm volatile("mfence" ::: "memory")std::atomic_thread_fence(memory_order_seq_cst)

    这对我来说很奇怪,因为memory_order_seq_cst原子函数和栅栏函数之间的用法似乎有很大不同。

现在我在MSVC 2015的标准库的头文件中找到这段代码,它实现了std::atomic_thread_fence

inline void _Atomic_thread_fence(memory_order _Order)
    {   /* …
Run Code Online (Sandbox Code Playgroud)

c++ x86 memory-barriers stdatomic

4
推荐指数
1
解决办法
2212
查看次数

NUMA上的mov + mfence安全吗?

我看到g ++生成一个简单的movfor x.load()mov+ mfencefor x.store(y).考虑这个经典的例子:

#include<atomic>
#include<thread>
std::atomic<bool> x,y;
bool r1;
bool r2;
void go1(){
    x.store(true);
}
void go2(){
    y.store(true);
}
bool go3(){
    bool a=x.load();
    bool b=y.load();
    r1 = a && !b;
}
bool go4(){
    bool b=y.load();
    bool a=x.load();
    r2= b && !a;
}





int main() {
    std::thread t1(go1);
    std::thread t2(go2);
    std::thread t3(go3);
    std::thread t4(go4);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    return r1*2 + r2;
}
Run Code Online (Sandbox Code Playgroud)

其中根据https://godbolt.org/z/APS4ZY go1和go2被翻译成

go1():
        mov     BYTE PTR x[rip], 1 …
Run Code Online (Sandbox Code Playgroud)

c++ x86 memory-model numa stdatomic

4
推荐指数
1
解决办法
149
查看次数

用于线程之间数据交换的内容是在一个Core上用HT执行的?

超线程技术是英特尔推出的一种同步多线程技术.

这些资源包括执行引擎,缓存和系统总线接口; 资源共享允许两个逻辑处理器更有效地相互协作,并允许停滞的逻辑处理器从另一个逻辑处理器借用资源.

在具有超线程的Intel CPU中,一个CPU内核(具有多个ALU)可以在同一时钟执行来自2个线程的指令.两个线程共享:存储缓冲区,缓存L1/L2和系统总线.

但是如果两个线程在一个Core上同时执行,则thread-1存储原子值,而thread-2加载此值,将用于此交换的内容:共享存储缓冲区,共享缓存L1/L2还是通常的缓存L3?

如果两个线程来自同一个进程(相同的虚拟地址空间)和两个不同进程(不同的虚拟地址空间),会发生什么?

Sandy Bridge Intel CPU - 缓存L1:

  • 32 KB - 缓存大小
  • 64 B - 缓存行大小
  • 512 - 行(512 = 32 KB/64 B)
  • 8路
  • 64 - 数组方式(64 = 512行/ 8路)
  • 6位[11:6] - 虚拟地址(索引)定义当前集合号(这是标记)
  • 4 K - 每个相同(虚拟地址/ 4 K)竞争同一组(32 KB/8路)
  • 低12位 - 对于确定当前设定数值很重要

  • 4 KB - 标准页面大小

  • 低12位 -每个地址的虚拟和物理地址相同

在此输入图像描述

concurrency x86 multithreading x86-64 hyperthreading

3
推荐指数
1
解决办法
721
查看次数

全局不可见的加载说明

由于存储负载转发,某些负载指令能否在全局范围内不可见?换句话说,如果加载指令从存储缓冲区中获取其值,则它永远不必从高速缓存中读取。
通常说来,当从L1D缓存读取负载时,该负载在全局范围内可见,因此,未从L1D读取的负载应使其在全局上不可见。

cpu-architecture memory-barriers cpu-cache

3
推荐指数
1
解决办法
297
查看次数

高速缓存一致性操作期间处理器是否停顿

假设变量a = 0

Processor1: a = 1
Processor2: print(a)
Run Code Online (Sandbox Code Playgroud)

Processor1首先执行它的指令,然后在下一个周期中,processor2读取变量以打印它。因此:

  1. Processor2将暂停,直到高速缓存一致性操作完成,它将打印1

    P1:   |--a=1--|---cache--coherence---|----------------
    P2:   ------|stalls due to coherence-|--print(a=1)---|
    time: ----------------------------------------------->
    
    Run Code Online (Sandbox Code Playgroud)
  2. 高速缓存一致性操作完成之前,processor2将运行,并且在此之前它将具有陈旧的内存视图。因此它将打印0?

    P1:   |--a=1--|---cache--coherence---|
    P2:   ----------|---print(a=0)---|----
    time: ------------------------------->
    
    Run Code Online (Sandbox Code Playgroud)

    换句话说,在高速缓存一致性操作完成之前,处理器可以拥有过时的内存视图吗?

multithreading caching cpu-architecture cpu-cache

3
推荐指数
1
解决办法
183
查看次数

内存屏障的传递性/累积性如何通过微体系结构实现?

我一直在阅读有关x86内存模型如何工作以及屏障指令在x86上的意义,并与其他体系结构(例如ARMv8)进行比较。在x86和ARMv8架构中,内存模型都遵循(无双关),即传递性/累积性,即如果CPU 1看到CPU0的存储,而CPU2看到CPU1的存储,则只有在CPU1看到CPU0的存储时才会发生,然后CPU2还必须查看CPU0的存储。我指的示例是保罗·麦肯尼(Paul McKenney)著名论文6.1节中的示例1和2(尽管相关,但他最新的性能手册《http://www.puppetmastertrading.com/images/hwViewForSwHackers》中也存在同样的问题。 pdf格式)。如果我理解正确,那么x86使用商店队列(或商店订单缓冲区)对商店进行排序(以及其他微体系结构优化),然后使其成为全局可见(即写入L1D)。我的问题是x86拱(和其他拱)如何实现(微架构)传递性?存储队列确保按特定顺序使特定CPU的存储在全局范围内可见,但是又如何确保一个CPU进行的存储排序与其他CPU进行的存储排序呢?

x86 x86-64 cpu-architecture memory-barriers micro-architecture

3
推荐指数
1
解决办法
66
查看次数

Synchronize-With 关系到底是什么?

我一直在阅读Jeff Preshing 的这篇关于 Synchronizes-With Relation 的文章,以及cpp 参考中的std::memory_order页面中的“Release-Acquire Ordering”部分,但我不太明白:

似乎标准有某种承诺,但我不明白为什么有必要。让我们以 CPP 参考中的示例为例:

#include <thread>
#include <atomic>
#include <cassert>
#include <string>
 
std::atomic<std::string*> ptr;
int data;
 
void producer()
{
    std::string* p  = new std::string("Hello");
    data = 42;
    ptr.store(p, std::memory_order_release);
}
 
void consumer()
{
    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_acquire)))
        ;
    assert(*p2 == "Hello"); // never fires
    assert(data == 42); // never fires
}
 
int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join(); t2.join();
}
Run Code Online (Sandbox Code Playgroud)

参考文献解释说:

如果线程 A 中的原子存储标记为 memory_order_release,并且线程 B …

c++ multithreading memory-model memory-barriers stdatomic

3
推荐指数
1
解决办法
723
查看次数

这个内存顺序正确吗?

std::atomic<bool> x{ false };
std::atomic<bool> y{ false };

// thread 1
y.store(true, std::memory_order_seq_cst);
x.store(true, std::memory_order_release);

// thread2
while (!x.load(std::memory_order_relaxed);
assert(y.load(std::memory_order_seq_cst)); // !!!
Run Code Online (Sandbox Code Playgroud)

断言会失败吗?我的理解是:虽然读取x是“放松的”,但一旦“线程2”看到“线程1”的写入,它就看不到yfalse因为写入y发生在写入之前x

内存顺序是从现实生活中的案例复制的,对于这个示例来说可能会更弱,但我没有改变它,以免错过任何微妙之处。

c++ multithreading atomic memory-barriers stdatomic

3
推荐指数
2
解决办法
353
查看次数

"完全记忆障碍"的反面是什么?

我有时会在关于内存排序的教程中看到术语"完全内存屏障",我认为这意味着以下内容:

如果我们有以下说明:

instruction 1
full_memory_barrier
instruction 2
Run Code Online (Sandbox Code Playgroud)

然后instruction 1不允许重新排序到下面full_memory_barrier,并且instruction 2不允许重新排序到上面full_memory_barrier.


但是完全内存屏障的反面是什么,我的意思是有什么像"半内存屏障"只能阻止CPU在一个方向上重新排序指令?

如果有这样的记忆障碍,我没有看到它的意思,我的意思是如果我们有以下指示:

instruction 1
memory_barrier_below_to_above
instruction 2
Run Code Online (Sandbox Code Playgroud)

假设这memory_barrier_below_to_above是一个阻止instruction 2重新排序到上面的内存屏障memory_barrier_below_to_above,因此不允许以下内容:

instruction 2
instruction 1
memory_barrier_below_to_above
Run Code Online (Sandbox Code Playgroud)

但是允许以下内容(这使得这种类型的内存屏障毫无意义):

memory_barrier_below_to_above
instruction 2
instruction 1
Run Code Online (Sandbox Code Playgroud)

assembly multithreading memory-barriers

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

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
查看次数