标签: memory-barriers

记忆障碍和大结构?

假设我有一个由100个字节组成的结构.我对以下代码有什么保证?

m_myLargeStruct = someValue; // copying 100 bytes
Thread.MemoryBarrier();

// Executed by another thread, after "Thread.MemoryBarrier" was called by the first thread
Console.WriteLine(m_myLargeStruct.ToString());
Run Code Online (Sandbox Code Playgroud)

内存模型是否保证在放置内存屏障后完成100字节的复制?或者内存屏障仅适用于处理器架构大小的类型?(32位为4字节,8位为64位).
这是volatile关键字仅适用于原始类型的原因吗?(如果我将一个8字节的成员声明为volatile,这意味着将使用一个互锁的instrinct来改变它的值?[因为32位机器上大于4字节的类型不能保证原子性]).

我希望我足够清楚.. :)
谢谢

c# multithreading volatile memory-model memory-barriers

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

放宽原子规则的(轻微)区别是什么?

在看到 Herb Sutters关于“原子武器”的精彩演讲后,我对放松原子的例子感到有些困惑。

我认为C++ 内存模型(SC-DRF = Sequentially Consistent for Data Race Free)中的原子在加载/读取时执行“获取”。

我知道对于负载 [和存储] 是默认值std::memory_order_seq_cst,因此两者是相同的:

myatomic.load();                          // (1)
myatomic.load(std::memory_order_seq_cst); // (2)
Run Code Online (Sandbox Code Playgroud)

到目前为止一切都很好,没有涉及放松的原子(在听完演讲后,我永远不会使用放松的原子。永远。承诺。但当有人问我时,我可能不得不解释......)。

但是为什么我使用时它是“宽松”的语义

myatomic.load(std::memory_order_acquire);   // (3)
Run Code Online (Sandbox Code Playgroud)

既然负载获取不是释放,为什么这与(1)和不同(2)究竟在这里放松?

我唯一能想到的就是我误解了load意味着acquire。如果这是真的,并且默认值seq_cst意味着两者,那不就意味着一个完整的围栏 - 没有任何东西可以传递该指令,也不能传递?我一定误解了那部分。

[并且对称地用于存储释放]。

c++ memory-model memory-barriers stdatomic relaxed-atomics

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

CPU乱序效果的测试程序

我写了一个多线程程序来演示英特尔处理器的乱序效果.该计划附在本文末尾.预期的结果应该是当handler1将x打印为42或0时.但是,实际结果总是为42,这意味着不会发生乱序效应.

我使用命令"gcc -pthread -O0 out-of-order-test.c"编译了程序.我在Intel IvyBridge处理器Intel(R)上运行Ubuntu 12.04 LTS(Linux内核3.8.0-29-通用)上的编译程序)Xeon(R)CPU E5-1650 v2.

有谁知道我应该怎么做才能看到乱序效果?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int f = 0, x = 0;

void* handler1(void *data)
{
    while (f == 0);
    // Memory fence required here
    printf("%d\n", x);
}

void* handler2(void *data)
{
    x = 42;
    // Memory fence required here
    f = 1;
}

int main(int argc, char argv[])
{
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, handler1, NULL);
    pthread_create(&tid2, NULL, handler2, NULL);

    sleep(1);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

c x86 synchronization memory-barriers barrier

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

这里需要记忆围栏吗?

考虑一下这段代码(从Simple-Web-Server中提取,但不需要知道库来回答这个问题):

HttpServer server;
thread server_thread;

server.config.port = 8080;
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    string content = "Hello world!"
    *response << "HTTP/1.1 200 OK\r\nContent-Length: " << content.size() << "\r\n\r\n" << content;
};

server_thread = thread([&server]() {
    server.start();
});
Run Code Online (Sandbox Code Playgroud)

HttpServer::default_resource是一个std :: unordered_map,根据我的理解,它不是线程安全的. port是一张未签约的短片.

假设我的C++内存栅栏的理解是正确的,server通过新的线程中看到的,可能不是一个有效的状态为主线可能不会写的变化port,并default_resource从其他线程访问内存.因此,server.start()可能无法正常工作.

要解决这个问题,我必须通过添加到atomic_thread_fences 来更改代码:

HttpServer server;
thread server_thread;

server.config.port = 8080;
server.default_resource["GET"] = [](shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request) {
    string content = "Hello world!"
    *response …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading memory-barriers

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

在 volatile 环境下分析 JIT 生成的 x86 输出

我写这篇文章是为了深入理解 Java 中的 volatile

public class Main {
    private int x;
    private volatile int g;


    public void actor1(){
       x = 1;
       g = 1;
    }


    public void actor2(){
       put_on_screen_without_sync(g);
       put_on_screen_without_sync(x);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我正在分析 JIT 为上述代码生成的内容。从我们在上一篇文章中的讨论中我们知道输出1, 0是不可能的,因为:


写挥发v的原因,每一个动作a前述v原因,那a之前是可见的(将被刷新到内存)v将是可见的。


   .................(I removed not important body of method).....

  0x00007f42307d9d5e: c7460c01000000     (1) mov       dword ptr [rsi+0ch],1h
                                                ;*putfield x
                                                ; - package.Main::actor1@2 (line 14)

  0x00007f42307d9d65: bf01000000          (2) mov       edi,1h
  0x00007f42307d9d6a: 897e10              (3) mov …
Run Code Online (Sandbox Code Playgroud)

java jvm volatile memory-barriers

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

原子释放可以“覆盖”吗?

假设我让atomic<int> i;线程 A 使用 memory_order_release 执行原子存储/交换。接下来,线程 B 使用 memory_order_release 执行原子存储。线程 C 执行原子 fetch_add(0, memory_order_acquire);

线程 C 是否从线程A 和 B仅从线程 B获取依赖项?

c++ atomic atomicity memory-barriers stdatomic

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

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

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

如果我们有以下说明:

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

_mm_sfence 内在函数和 SFENCE 指令之间有什么关系?

我正在试验非时间指令,并且已经熟悉具有普通加载/存储的围栏如何操作。

Intel 定义了一个与非时间操作相关的内在,_mm_sfence,手册将其定义为:

保证每个前面的商店在任何后续商店之前都是全局可见的。

我对这个操作有一些疑问。

  1. 这只是插入 SFENCE 指令吗?如果不是,这意味着什么?
  2. 如果这不仅仅是一个 SFENCE,那么 SFENCE 指令是否也带有非临时存储的排序保证?

x86 memory-barriers

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

为什么我们需要读和写屏障?

为什么我们需要定义两种具有相同实现的障碍?

例如,io_uringLinux 中的这段代码:

#if defined(__x86_64) || defined(__i386__)
#define read_barrier()  __asm__ __volatile__("":::"memory")
#define write_barrier() __asm__ __volatile__("":::"memory")
#else
Run Code Online (Sandbox Code Playgroud)

c x86 gcc linux-kernel memory-barriers

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

除了提供必要的保证外,硬件内存屏障是否可以更快地了解原子操作?

TL;DR:在生产者-消费者队列中,放置一个不必要的(从 C++ 内存模型的角度来看)内存栅栏或不必要的强内存顺序是否有必要以牺牲可能更差的吞吐量为代价来获得更好的延迟?


C++ 内存模型是在硬件上执行的,方法是使用某种内存栅栏来实现更强的内存顺序,而不是将它们放在较弱的内存顺序上。

特别是,如果生产者这样做store(memory_order_release),而消费者使用 观察存储的值load(memory_order_acquire),则加载和存储之间没有围栏。在 x86 上根本没有栅栏,在 ARM 上栅栏是在存储之前和加载之后进行放置操作。

没有围栏存储的值最终会被没有围栏的负载观察到(可能在几次不成功的尝试之后)

我想知道在队列的两侧放置围栏是否可以更快地观察到值?如果有围栏和没有围栏,延迟是多少?

我希望只有一个循环load(memory_order_acquire)pause/yield限制为数千次迭代是最好的选择,因为它无处不在,但想了解原因。

由于这个问题是关于硬件行为的,我希望没有通用的答案。如果是这样,我主要想知道 x86(x64 风格),其次是 ARM。


例子:

T queue[MAX_SIZE]

std::atomic<std::size_t>   shared_producer_index;

void producer()
{
   std::size_t private_producer_index = 0;

   for(;;)
   {
       private_producer_index++;  // Handling rollover and queue full omitted

       /* fill data */;

      shared_producer_index.store(
          private_producer_index, std::memory_order_release);
      // Maybe barrier here or stronger order above?
   }
}


void consumer()
{
   std::size_t private_consumer_index = 0;

   for(;;)
   {
       std::size_t observed_producer_index = shared_producer_index.load( …
Run Code Online (Sandbox Code Playgroud)

c++ x86 arm cpu-architecture memory-barriers

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