问题标签 [memory-model]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
1 回答
12039 浏览

c++ - C++11 memory_order_acquire 和 memory_order_release 语义?

http://en.cppreference.com/w/cpp/atomic/memory_order和其他 C++11 在线参考资料,将 memory_order_acquire 和 memory_order_release 定义为:

  • 获取操作:当前线程中的任何读取都不能在此加载之前重新排序。
  • 释放操作:当前线程中的任何写入都不能在此存储之后重新排序。

这似乎允许在获取操作之前执行获取后写入,这对我来说似乎很奇怪(通常的获取/释放操作语义限制了所有内存操作的移动)。

相同的在线资源(http://en.cppreference.com/w/cpp/atomic/atomic_flag)建议可以使用 C++ 原子和上述宽松的内存排序规则构建自旋锁互斥锁:

有了这个锁定/解锁的定义,如果 memory_order_acquire/release 确实以这种方式定义(即,不禁止重新排序后获取写入),下面的简单代码不会被破坏:

是否可以执行以下操作:(0) lock, (1) x = 1, (5) x = 0, (2) PANIC ?我错过了什么?

0 投票
3 回答
16261 浏览

c++ - 与 `std::mutex` 同步是否比使用 `std::atomic(memory_order_seq_cst)` 慢?

在互斥体上使用原子的主要原因是互斥体很昂贵,但是使用默认的内存模型,这atomics不是memory_order_seq_cst一样昂贵吗?

问题:使用锁的并发程序能否与并发无锁程序一样快?

如果是这样,除非我想用于原子,否则可能不值得付出努力memory_order_acq_rel


编辑:我可能遗漏了一些东西,但基于锁的不能比无锁更快,因为每个锁也必须是一个完整的内存屏障。但是对于无锁,可以使用比内存屏障限制更少的技术。

所以回到我的问题,无锁是否比基于新 C++11 标准的默认锁更快memory_model

“以性能衡量时,无锁 >= 基于锁”是真的吗?假设有 2 个硬件线程。


编辑2:我的问题不是关于进度保证,也许我在上下文之外使用“无锁”。

基本上,当您有 2 个线程共享内存时,您需要的唯一保证是,如果一个线程正在写入,则另一个线程无法读取或写入,我的假设是简单的原子compare_and_swap操作比锁定互斥锁快得多.

因为如果一个线程甚至从未接触过共享内存,您最终将无缘无故地一遍又一遍地锁定和解锁,但使用原子操作您每次只使用 1 个 CPU 周期。

关于评论,当争用很少时,自旋锁与互斥锁是非常不同的。

0 投票
3 回答
686 浏览

c++ - C++ 条件语句是否携带从条件表达式到语句的依赖关系?

我是在内存模型的意义上专门问的。http://en.cppreference.com/w/cpp/atomic/memory_order

我问是因为我想知道我是否可以std::memory_order_consume在下面使用 a :

mLocalMemPtr1 和 2 以及 mAtomicMemPtr 是指向共享缓冲区的指针。

在生产者线程中,我正在做:

在消费者中:

那么依赖链是 tempMemPtr -> mLocalMemPtr2 -> test -> doSomeLongRunningThing 吗?

我特别担心B之前可能会被执行A。我知道我可以使用 a std::memory_order_acquire,但如果条件语句导致内存顺序依赖,我可以使用 consume (更轻量级)。

0 投票
1 回答
118 浏览

memory - 关于内存模型的问题

当我阅读与编译器相关的书时,我看到有两种主要的内存模型。

注册到注册模型和内存到内存模型。

在书中,它说寄存器到寄存器模型忽略了机器对寄存器数量的限制,并且编译器后端必须插入加载和存储。是因为寄存器到寄存器模型可以使用虚拟寄存器......并且这个模型保留了所有可以存储在寄存器中的值,所以在完成之前它必须插入加载和存储(与内存相关)?

此外,在内存到内存的部分,这本书说编译器后端可以删除冗余的加载和存储。这是否意味着模型必须删除多余的内存使用以进行优化?

0 投票
4 回答
1172 浏览

c# - 内存模型:防止存储释放和加载获取重新排序

众所周知,与 Java 的 volatile 不同,.NET 的 volatile 允许使用来自另一个位置的以下 volatile 读取来重新排序 volatile 写入。当出现问题时 MemoryBarier建议放在它们之间,或者Interlocked.Exchange可以用 volatile write 代替。

它可以工作,但MemoryBarier在高度优化的无锁代码中使用时可能会成为性能杀手。

我想了一会儿,想到了一个主意。我希望有人告诉我我是否走对了路。

所以,思路如下:

我们希望防止这两个访问之间的重新排序:

从 .NET MM 我们知道:

为了防止写入和读取之间不必要的重新排序,我们从刚刚写入的变量中引入了一个虚拟的 volatile 读取:

在这种情况下,B不能用A重新排序,因为它们都访问同一个变量, C不能用B重新排序,因为两个 volatile 读取不能相互重新排序,并且C不能用 A重新排序。

问题是:

我对吗?这种虚拟易失性读取可以用作这种情况下的轻量级内存屏障吗?

0 投票
1 回答
6613 浏览

java - 因果一致性与顺序一致性有何不同?

我知道在顺序一致性中,所有流程都必须按顺序处理。例如:

所以,我们可以得到 x=1, z=5, y=2, p=3 或 z=5, p=3, x=1, y=2。但重要的是 p 只能在 z 执行后才能执行,对吗?

因果一致性呢?我看不出有什么不同。JAVA 或 C 中的任何草图或代码都会很棒。谢谢你。

0 投票
1 回答
418 浏览

x86 - 受保护平面模型中的 CS 与 DS 值

受保护平面模型中给定进程的 CS 和 DS 段寄存器是否保持相同的值?
换句话说,在同一个程序中执行以下代码序列
mov dword ptr [0x7fffffff], ebx
并且
org 0x7fffffff
...some instruction ...

参考同一个位置?(如果是这样,那么在受保护的平面模型中拥有单独的 CS 和 DS 的目的是什么?)谢谢。

0 投票
3 回答
1781 浏览

c++ - 放松的原子规则有什么(轻微的)区别?

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

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

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

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

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

既然负载获取不是释放,为什么这与(1)and不同(2)这里真正放松的是什么?

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

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

0 投票
1 回答
1929 浏览

java - 使用 volatile 和 synchronized 时,内存刷新或发布到各个线程的范围是什么?

这个问题仅涉及内存可见性,而不是发生在之前和之后。Java中有四种方法可以保证一个线程中对内存的更改对另一个线程可见。(参考http://gee.cs.oswego.edu/dl/cpj/jmm.html

  1. 写入线程释放同步锁,读取线程随后获取相同的同步锁。
  2. 如果一个字段被声明为易失性,则写入它的任何值都会在写入线程执行任何进一步的内存操作之前被写入线程刷新并使其可见(即,出于手头的目的,它会立即刷新)。
  3. 线程第一次访问对象的字段时,它会看到该字段的初始值或自其他线程写入以来的值。
  4. 当线程终止时,所有写入的变量都被刷新到主内存。

根据Java Concurrency in Practice,有关此类问题的圣经:

volatile 变量的可见性影响超出了 volatile 变量本身的值。当线程A写入 volatile 变量并且随后线程B读取相同的变量时,在写入 volatile 变量之前对A可见的所有变量的值在读取 volatile 变量后对B可见。

不稳定的问题

这是否意味着 JVM 实际上会跟踪 volatile 变量的读取和写入,以便知道如何将内存从A刷新到B而不是AC?所以A写入变量,然后C从变量中读取,然后B从变量中读取,刷新是在AB以及AC之间按线程完成的,但不是 BC?或者,这是否意味着所有缓存的内存都被刷新,而不管线程如何?是仅刷新 volatile 变量,还是所有缓存内存?

同步问题

对于synchronized关键字flushing,它表示只有在锁内更新的内存才能保证发布到其他线程。这意味着在下面的代码中,两个线程运行method(),离开同步块将刷新staticVar2到另一个线程,但不是 staticVar1,对吗?

此外,在 中method2(),如果另一个线程正在执行,则同步结束differentLock可能会导致发生前发生后发生问题method()。但是,问题在于可见性。如果线程A执行method,然后线程B稍后执行,即使两个线程没有在同一个锁上同步,method2()值是staticVar2A发布到B吗?

静态问题

在我看来,如果staticVar1从未更新到其他线程,那么任何程序中的所有静态变量都需要volatile声明,或者只能在synchronized块中访问。这似乎相当苛刻,但它是正确的吗?我确实在我的时间里看到了很多不同步的静态变量。

总之

  1. 易失性读写是否将所有内存刷新到所有线程,还是仅在两个访问线程之间?无论答案是什么,是所有内存都被刷新还是只有 volatile 变量?
  2. 退出同步块时是否会刷新所有更改的内存,还是仅刷新块内更改的内存?如果不是所有内存都被刷新,线程同步的锁对象是否必须相同才能看到值(即锁对象对内存可见性有任何影响)?
  3. 两个线程访问的所有静态变量都必须同步吗?
0 投票
1 回答
287 浏览

c++ - C ++ 11中的原子是否会阻止编译器从共享变量中重新读取?

我正在寻找第二次 Herb 精彩的“原子武器”演讲,我试图围绕经历整个内存模型/顺序一致性故事的概念来思考。现在有一件事在概念层面困扰着我。谈话的要点是,通过使用原子,我们可以“提示”编译器关于线程之间的交互,否则编译器将无法检测到。

所以我开始担心以下情况:

在这种情况下,正如 Hans Bohem 在“如何使用“良性”数据竞争错误编译程序”中所指出的那样(变量名称已针对上面的代码片段进行了相应调整):

如果编译器决定它需要在两个测试之间溢出包含 local_copy_of_shared_var 的寄存器,它可能会决定避免存储该值(毕竟它只是 shared_var 的副本),而是简单地重新读取 shared_var 的值对于涉及 local_copy_of_shared_var 的第二个比较。

[...] 核心问题源于编译器利用了变量值在没有显式赋值的情况下无法异步更改的假设。如果在我们的设置中语言规范不允许数据竞争,那么这种假设是完全合理的。在没有数据竞争的情况下,不可能进行这样的异步更改

现在,由于原子(使用默认的 seq_cst 内存排序)应该保证没有数据竞争,并且因为它们是编译器的“提示”,即不同线程之间存在此类变量的交互,有人可能会争辩说在前面使用原子片段会阻止编译器从shared_var插入此类“重新读取” ​​,而是将local_copy_of_shared_var视为“一次性”快照,以避免两个测试之间的不一致?

我认为我的推理有问题,因为在常识的驱动下,我不会认为仅在此处使用原子可以保证编译器将采取措施,以使local_copy_of_shared_var在两次测试之间不会得到更新。另一方面,正如 Herb 在他的演讲中所说,内存模型现在保证编译器在使用原子时不应该添加任何虚假的内存操作,这(将这种情况视为虚假读取)再次表明这个例子现在是“安全的”。我很困惑,想听听社区的意见,如果我的推理中有一些错误,我可能会得到纠正。