ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c++ memory order

2022-04-22 22:33:14  阅读:276  来源: 互联网

标签:load cache c++ 线程 内存 memory order


https://sf-zhou.github.io/brpc/brpc_01_bthread.html

memory_order_relaxed

不保证同步操作,不会将一定的顺序强加到并发内存访问上,只保证原子性和修改顺序一致性

x = y = 0
// Thread 1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
// Thread 2:
r2 = x.load(std::memory_order_relaxed); // C 
y.store(42, std::memory_order_relaxed); // D

D 能出现在 A前面,B 能出现在 C前面,D也能出现在C前面,因为编译器重排和运行期乱序,最后的结果是可能是 r1 == r2 == 42 的

松弛内存序通常用于计数器的递增,例如对 shared_ptr 引用计数的递增,但是递减不是。

总结:存在一个全序修改序,在同一线程对同一个内存地址的操作是不会重排的。

x.compare_exchange_weak(
						oldVal, // expected
						oldVal+1, // desired
						memory_order_relaxed);

memory_order_release-memory_order_acquire

memory_order_release 的 store 的线程 A,和 memory_order_acquire 的 load 的线程 B。

所有的写内存的操作(非原子的,松弛内存写)都将会会 happened-before 于 store 线程 A,在 A 看来,
所有的写操作都完成了,对于线程B来说,所有的写都将变得可见,也就是说,一旦B完成了 load 操作,线程B能看到所有
线程A写到内存的东西。这个承诺仅在一个 load 线程B返回了 A 所 store 的值,或者在接下来的 release 序列中。

一个线程内任何 happened-before 于 store 的事件,都将在 load 的线程中作为副作用(可见),换句话说,load 的副作用让 store 前的事件变得可见。

同步建立在相同的变量上进行 release 和 acquire,其他未同步的线程将会见到不同的内存序

在强序的系统上,比如在X86,IBM上,release-acquire 是大多数的指令操作是自动会应用的,不需要提供其他的同步指令,在一些弱序架构上,比如ARM和Itanium上就需要在load前加上memory fence,还有会影响一些编译器的优化。
这种序通常会用在mutex和spin lock上,也就是说当修改线程在临界区执行完成后,任何修改对于其他线程都是可见的。

总结:所有在 store-release 所在线程的操作对于 load-acquire 都是可见的。

memory_order_release-memory_order_consume

(相关的细节还在修订,并且暂时不鼓励)

和 memory_order_release-memory_order_acquire 的区别在于,只会在依赖的变量上是可见的。当 memory_order_consume 的 load 完成后,标记 memory_order_release 在原子变量的修改对于所有在 load 之后且在这个变量上的函数和操作都是可见的。

DEC Alpha 的主流 cpu 都是自动支持这种序的。

是 release-acquire 的轻量版本,带入了依赖,consume 依赖某个变量 X,那么在 store-release 的线程中对变量X的操作都是先发于(happend-before) consume

Sequentially-consistent ordering

原子操作默认是seq_consist

    bool
    load(memory_order __m = memory_order_seq_cst) const noexcept
    { return _M_base.load(__m); }

memory_order_seq_cst 不仅仅用 memory_order_release-memory_order_acquire 的顺序排序,还将对带有 memory_order_seq_cst
的原子操作提供一个单独的全序修改顺序 single total modification order

操作B memory_order_seq_cst 的 load 原子变量 M,遵从下面这些条件:

  • 最后一次修改 M 的操作 A 的结果,将会 single total order 的 B前出现。
  • 如果 A 不是 memory_order_seq_cst,则B可能观察到不是

single total order 全序偏序:
偏序指的是只能为系统中的部分事件定义先后顺序。这里的部分其实是有因果关系的事件。
全序是指所有的事件都可以区分先后顺序。无论是真实或是虚拟世界,这都是受欢迎的。因为,所有的事件都有了一个统一的评判标准,我们一直欢迎统一而拒绝分裂。
lamport http://zhangtielei.com/posts/blog-time-clock-ordering.html

Sequential ordering 可能对于多个 producer 和多个 consumer 有效,并且观测到所有 producer 的都保持一个一致的顺序。Total sequential ordering 会要求一个完全的内存栅栏指令到多核系统中,会成为系统的瓶颈。

其他:
https://www.think-cell.com/en/career/talks/pdf/think-cell_talk_memorymodel.pdf

Dekker's algorithm 和 Peterson

用途

正确的线程间同步
实现lock-free算法
![[Pasted image 20220422175119.png]]

cpu cache

cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size

查看Cache Line 的大小,会顺序加载64字节

  • 可以测试false shared

如何知道在内存还是在cache内?内存和cache如何对应
一种是 Direct Mapped Cache 来做,通过对内存取模映射到 cacheline上
多个内存缓存在一起怎么办?cache line中有组标记 tag,区分不同的内存块,和有效位 valid,判断是否需要强制从内存取值。内存地址被分成 组标记 + 索引 + 偏移量 得到在 cache block 的地址。

缓存命中率提高:
unlikely
sched_setaffinity

什么时候才把cache的数据写回内存?

  • 写直达(write through):把数据同时写入内存和 Cache 中,这种方法称为写直达(Write Through,写操作会很花费事件
  • 写回(write back):新数据仅仅被写入到 cache block,只有当 block 需要被替换的时候,才会刷到内存中去。新数据写入到 cache block,当前cache block 会被标记为 dirty,只有写入的地址不是当前cache block所属组标记的时候,才会重刷缓存。如果是dirty,将数据写回到内存,否则直接加载新的 cache block。

缓存一致性

https://zhuanlan.zhihu.com/p/269221065

内存序 相关资料pdf阅读

Memory Models for C/C++ Program:
https://arxiv.org/pdf/1803.04432.pdf

标签:load,cache,c++,线程,内存,memory,order
来源: https://www.cnblogs.com/pokpok/p/16180793.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有