(图来自 CMU 15-418 Spring 2020 和 CS152)

当我们涉及多核的时候,我们的多个核可能会共享一份内存:

单个变量的访问可能会有问题:

我们访问 memory, 可能在直觉上会是:

读到最近一级别存储 x 的上一个写入的值

但是,memory 会遇到的问题是

  1. 有 global 的 storage 和 local cache,而且这两个可能是不一致的

    1. 所以我们有 write back 和 write through 的策略,来 trade 性能和 consitent
  2. 有多个 local cache 的话,可能也会导致这种视图上的不一致

但是说到底,“读到上一个写入的值”,或者,“写入时间最近的值”,究竟是什么语义呢:

事实上,我们之前介绍过 CPU Cache, 现在给出一个 L1 L2 L3 的视图

显然,现在我们对同一个变量的访问,如上上张图所示,我各个处理器本身是顺序处理的,而它们在一起,会得到一个诡异的偏序,而 P1 P2 P3 的 x 值和内存中的 x 值是 inconsistent 的。这是问题所在:我们无法定义“上一个”逻辑意义上是什么。

要在保证性能的同时,也作出各个处理器对 x 的值读写的偏序保证,我们需要 coherence

Single CPU System: I/O

单核 CPU IO 的时候,假设要读/写数据,可能会:

  1. processor 写到了 write-buffer 里,而网卡可能没有读到 buffer 中的数据,而是读到了 memory 中的 stale data,造成写 stale data
  2. 网络中数据写到了 memory 中,通知 processor, processor lw 读到了 cache,造成读 stale data

某种意义上说,这也是一种不一致导致的,为了解决这个问题,需要下列的支持

  1. CPU 写的时候可以写到和设备的 shared buffer 中,而不是本身的 write buffer。让设备能够读到正确信息
  2. OS 可以把读写的 page 设置成不可 cache 的,同时,IO 完成的时候 flush cache page
  3. 在生产中,DMA transfer 的频率远比 lw sw 少,所以可以接受代价较高。

Coherence

终于到了 conherence 了,那么我们来讲讲,memory coherence,我必须要说,下面这张图理的绝对很清晰了:

.

  1. 单个 Processor 对单个变量 x 的读写是顺序的
  2. write propagation: P 对一个变量的写入必须最终对其他 Process 可见
  3. write serialization : 写有全局的顺序

以上便是违背 write serialization 的例子,对于 x 写入 “a" "b", P3 P4 观察到的顺序不一致。

那么,在这种假设下,假设有三个线程 t0, t1, t2:

// t0
for (i = 0; i < N; i += 2) {X = i;
}// t1
for (j = 1; j < N; j += 2) {X = j;
}// t3
for (k = 0; k < ..; k++) {s[k] = X;
}

那么,数组 s 中,可以看到我写的脑瘫程序可以输出:

26980 626437 1226236 1806376 2449353 3083919 3382384 3734980 4165338 5994059
4872578 7435291 5586348 8893777 6262932 6539670 6875646 7205346 7535204 7873218
8256550 8658202 14268993 14634249 9901488 10347038 15693957 16119785 16529365 17135341
17836189 12867162 19251979 19962671 13929908 14261510 14598770 22698103 15331624 24051107

这里奇数/偶数都是强 order 的,但是他们之间并没有什么保证,只能保证全局有一致的 order

(题外话:但是我看到这有个问题,就是 atomic + relaxed 和 non-atomic 有什么区别,瞅了眼: https://stackoverflow.com/questions/63810298/what-is-the-difference-between-load-store-relaxed-atomic-and-normal-variable 这似乎保证单个操作的原子性,比如我们 RV32I 的 auipc 和 add...)

关于实现 Cache Coherent,是下一节的内容,我们再次先看 Memory Model 吧。

Memory Consistency

最实用、最有挑战性、最令人困惑的部分来了。我们现在有了变量 x 的 coherence,我们还需要什么呢?

Cache Coherence 保证了单个 cache block 的 loads and stores 是按照定义运行的,但是我们假设有一个 produce-consume:

// produce
value <- 5
flag <- ok// consume
wait until flag is ok
-- 操作 value

那么,很尴尬的是,你会发现 cache coherent 没有提供可靠的保障。

“Memory Consistency Model” (sometimes called “Memory Ordering”): do all loads and stores, even to separate cache blocks, behave correctly?

实际上,你会发现问题的来源很多,而且来源不是上层应用,而是我们讲了2节的 cache write-buffer multi-level cache 这些给我们带来利好的东西,和 OoO 执行、编译器乱序等正常对程序员透明,现在全冒出来了的东西。

Uniprocess Memory Model

单个程序按 program order 的语义执行、读写

这图相当于我们 Part2 说的。

那么,在并行的时候,也就是我们说的 producer-consumer 中,可能会有下图的问题:

write buffer 和 OoO 等乱序不能保证写入会被顺序的传播,没有 happens-before 的语义。

更要命的是,cache 也会在其中添乱:(当然不止是 cache,所以回头来,你要理解到,volatile 这个词在 C/C++ 中是危险的,不能认为它们读写内存就万事大吉了,不过我也不太清楚搞 NVM 的大佬们会不会用到这个)

由于 P3 上有 A 的 cache, 它并不会从内存中读取应读的值,cache coherent 也没被违背,但是这会儿程序的语义就没法保证了!

更要命的是,还有编译器的乱序,关于编译器乱序可以参考没有使用 atomic 的 LevelDB 的 SkipList, 它只是写了一个编译器 barrier,而没有用 CPU 的 barrier 和指令。

Sequential Consistency: SC

这个是老熟人了:

Formalized by Lamport (1979)

  • accesses of each processor in program order
  • all accesses appear in sequential order

我理解就是简单说所有操作有一个全序,就所有操作偏序变全序。你可以用 seq_cst 来体验一下(

这个时候,写入要求:

For each processor, delay start of memory access until previous one completes: each processor has only one outstanding memory access at a time

读取要求

a read completes when its return value is bound

上述会带来严格的限制和可靠性,但是性能...

只能用悲剧来形容(

于是我们有 relaxed 一些的模型,例如 TSO/PSO 等:

注:x86的 memory model 类似 TSO,只允许 store-load 乱序。

这在优化中获得了权衡:

但是,这个时候,我们仍然要面对最初的那个问题:

// produce
value <- 5
flag <- ok// consume
wait until flag is ok
-- 操作 value

Data Race:

  • two conflicting accesses on different processors
  • not ordered by intervening accesses

显然,我们对 flag 的操作本身没有经过同步,这个在仅允许 store-load 乱序的系统中,甚至没啥问题(不考虑编译器乱序)。但是,在 RISC-V 这种系统,或者 ARM 里,你就等死吧。我们需要一些机制来同步:

Properly Synchronized Programs:

  • all synchronizations are explicitly identified
  • all data accesses are ordered through synchronization

我们可以使用:

  1. memory barrier
  2. 显式的锁,来隐式提供 fence

逻辑如下:

  1. x86 的 xchg 显式的帮你完成这一切,你可以 acquire 来获得锁,在互斥区维护 invariant,release 来释放锁。锁前,锁中,锁后都是可以 re-order 的,但是它们不能越过锁。
  2. MFENCE LFENCE MFENCE , fence会 stall 程序,把之前的操作执行完,来创建偏序。

上述例子中,fence w, wfence r, r 构建了一个偏序关系。sw data, (xdatap) 保证被 lw xdata, (xdatap) 感知了。

RISC-V 的支持:RV32A

RISC-V 具有宽松的内存一致性模型(relaxed memory consistency model),因此其他线程看 到的内存访问可以是乱序的。图 6.2 中,所有的 RV32A 指令都有一个请求位(aq)和一个 释放位(rl)。aq 被置位的原子指令保证其它线程在随后的内存访问中看到顺序的 AMO 操 作;rl 被置位的原子指令保证其它线程在此之前看到顺序的原子操作。想要了解更详细的 有关知识,可以查看[Adve and Gharachorloo 1996]。

RV32A 没有直接提供了 CAS FAA 的指令,而是提供了两套指令。RV32A 认为这样有更好的可扩展性:

编译器乱序和语言的 memory_order

https://chromium.googlesource.com/external/leveldb/+/v1.15/port/atomic_pointer.h#61

除了内存,编译器也会完成乱序。最佳的例子是 LevelDB 之前的编译器 barrier。

语言会提供对应的语义,如 C++ 的六种 order (实话说我 consume 那几个完全不懂): https://en.cppreference.com/w/cpp/atomic/memory_order

或者简单点可以看看 Golang 的(

References

  1. CMU 15-418 Spring 2020
  2. UCB CS152 Spring 2020
  3. CPU Cache and Memory Ordering 何登成
  4. RISC-V Reader
  5. Computer Organization and Design RISC-V edition
  6. 我坦白了,上面的我都没看完(

laravel order 按时间升序_Cache and Related Part3: Coherence amp; Order相关推荐

  1. MYSQL 数据库基础(三 流程函数的学习,多行函数相当于统计计算等等、group by的使用、goup by衍生出的having、排序关键字order by、升序ASC、降序DESC、模糊查询等)

    流程函数 类似与java中的判断情况 的学习 实际应用: -- 满足条件执行 第一个逗号后面的 否则执行第二个逗号 SELECT ename,sal,IF(sal<2500,'低于2500',' ...

  2. mysql count order by_【数据库】mysql中count(), group by, order by使用方法分享

    本文主要和大家分享mysql中count(), group by, order by使用方法,mysql中order by 排序查询.asc升序.desc降序,group by 分组查询.having ...

  3. Sentinel采用SphO方式定义资源,报错:The order of entry exit can‘t be paired with the order of entry

    (1)问题描述 在搭建sentinel工程的时候,采用SphO方式手动定义资源,启动工程后,访问资源,发现报错,报错内容大致如下: o.s.web.servlet.HandlerExecutionCh ...

  4. laravel mysql超时时间_连接到数据库时Laravel连接超时

    我将应用程序从我的开发机器移动到测试服务器.连接到我的本地开发mysql数据库时,一切都按预期工作.尝试连接到我们的测试服务器时,请求超时45秒后返回500错误.连接到数据库时Laravel连接超时 ...

  5. linux按时间升序排列文件,Linux展示以时间生升序显示文件

    Linux显示以时间生升序显示文件 Linux显示以时间生升序显示文件 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ ls -lnt 总用量 56 -rw-r ...

  6. laravel按月/时间区间查询数据

    public function payRollList($time){$member_phone = Session::get(phone');$engineer = Engineer::getEng ...

  7. laravel mysql超时时间_Laravel数据库MySQL查询需要很长时间

    我有一个连接到我的数据库的页面,但收集所有数据后页面加载大约需要1分钟. 有什么我做错了,或者我能做些什么来加快这个过程? 调节器 class ReportSummaryController exte ...

  8. mysql order by sql注入_mybatis中#{}和${}的区别及order by的sql注入问题

    前言略,直奔主题.. #{}相当于jdbc中的preparedstatement ${}是输出变量的值 简单的说就是#{}传过来的参数带单引号'',而${}传过来的参数不带单引号. 你可能说不明所以, ...

  9. 使用order by排序判断返回结果的列数,order by排序判断字段数原理详解

    「作者主页」:士别三日wyx order by排序猜解列数原理详解 一.order by的两种使用方式 1)按照字段名排序 2)按照索引排序 二.order by怎么判断字段数? 1)正常的排序 3) ...

最新文章

  1. MicroSoft的Office使用攻略
  2. java泰坦宙斯之战程序_泰坦 - 宙斯之战 相关知识点: 1. 使用随机类 2. 类的成员变量 3. 类的成员方法 4. 带参构造器_电商题库2017(学测)答案_学小易找答案...
  3. 大型网站系统架构实践(四)http层负载均衡之haproxy实践篇(一)
  4. Linux下高速缓存DNS的配置
  5. 【报告分享】2019-2020广告主KOL营销市场盘点及趋势预测.pdf(附下载链接)
  6. python最简单的游戏源代码_Python 练习: 简单角色游戏程序
  7. [转载] Python基于机器学习方法实现的电影推荐系统
  8. 美团在ACL2021上提出基于对比学习的文本表示模型,效果提升8%
  9. PHP中去除换行解决办法小结(PHP_EOL)
  10. java|Android仿Form表单以post方式提交文本和文件
  11. 23种经典设计模式UML类图汇总
  12. IEEE 投稿参考文献格式bib
  13. 程序猿来找找自己的目标
  14. C语言中的连等式解析
  15. Windows删除流氓软件方法记录
  16. 第39章 连续时间信号与系统的S域分析
  17. lotus - 深入理解时空证明的 golang 实现部分(PoSt)
  18. 串口数据实时处理:定时器+串口 判断串口数据接收完成
  19. 特别策划|5G最新进展深度解析2022版—国内市场篇(65页附下载)
  20. 为什么苹果日历不能设置日程_iPhone 小技巧:在“日历”中添加日程和设置提醒_iPhone技巧...

热门文章

  1. Linux学习总结(64)——DBA常用的Linux命令汇总
  2. 权限系统设计学习总结(3)——多账户的统一登录
  3. Tomcat学习总结(20)—— Tomcat启动脚本收藏
  4. Maven学习总结(49)——Maven Profile详解
  5. Docker学习总结(24)——在Docker中监视Java应用程序的5种方法
  6. Spring MVC学习总结(14)——SpringMVC测试框架之mockMVC详解
  7. Java Web学习总结(3)——Servlet详解
  8. 一下代码不符合php,各位帮我看看哪里错了一直提示错误,谢谢大家。可把代码复制一下...
  9. Wget 命令用法详解
  10. maven项目引入新依赖问题