程序员实用硬件体系结构归纳(一):计算

文章目录

1. 硬件体系结构

硬件关注大致CPU、CPU Cache(L1, L2, L3)、内存、SSD、普通硬盘HDD、网络网卡这几个层次,可以分为计算(CPU+内存)、存储(内存+SSD+HDD)、网络(内存+网络网卡)

2. CPU架构

CPU核内主要技术点包括Super pipeling、Superscalar
、Simulaneous Multi-Threading(SMT)、out-of-order等,目前CPU都使用多核架构,例如来自Intel Core i7的Nehalem架构Fetch to retire,

CPU使用IMC接口与内存互联,使用QPI连接多个处理器((processors 比如Nehalem Quadcore)

2.1 性能指标

参考性能指标包括:

  1. 主频: CPU的时钟频率,内核工作的时钟频率
  2. 外频: 系统总线的工作频率
  3. 倍频: CPU外频与主频相差的倍数
  4. 前端总线: 将CPU连接到北桥芯片的总线
  5. 总线频率: 与外频相同或者是外频的倍数
  6. 总线数据带宽: (总线频率 * 数据位宽) / 8

相关组件有L1,L2,L3 cache (缓存数据与指令)
L1,L2: core独占; 带宽:20-80GB/S;延时:1-5ns
L3: core之间共享; 带宽:10-20GB/S;延时:10ns
Cache line size: 64 Bytes

QPI接口
Intel中,连接一个CPU中的多个处理器,直接互联 QPI带宽:~20GB/s

3. CPU Cache

Cache Line是Cache与内存Memory交换的最小单位,X86架构的CPU基本为64B,以组相联的形式组织

假设我们有一个512字节的一级缓存,那么按照64B的缓存单位大小来算,这个一级缓存所能存放的缓存个数就是512/64 = 8个。cpu 的cache line 会有很多, 然后不同的 cache line 组成一个set, 也就是说同一个set 里面的多个cache line, 如果一个set 有8个cache line, 那么这个set 能够缓存的数据就是512 byte

一种方案Write Invalidate写导致其他CPU L1/L2的同一Cache Line失效,另一种Write Update写同时更新其他CPU L1/L2的同一Cache Line

写策略通常有Write Back新脏数据先读到Cache然后修改,Write Through新脏数据直接写到Memory

主要问题是Cache Coherence算法,比如MESI, MOESI等

MESI协议,有四种状态:

  • Invalid 无数据,
  • Shared 与Memory一致数据且多节点共享,
  • Exclusive 与Memory一致数据且单节点持有,
  • Modified 最新修改数据且单节点持有。

有六种交互信息:

  • Read
  • Read Reponse
  • Invalidate
  • Invalidate Acknowledge
  • Read Invalidate
  • Writeback

MOESI协议,五种状态:owned 与Shared共存,持有最新修改且Memory过期

这些协议作用于CPU Cache L1/2和内存,与Register无关

3.1 程序优化

  1. 尽可能的顺序访问内存, 不要随机访问内存. 因为一个cache line 的大小是64btye, 如果访问的内存不连续, 那么就浪费了这个prefetch 的内容, 同样对于写入也是如此, 顺序写入, 可以批量提交给cache, 能够最大限度利用总线带宽.
  2. 在组织一个struct 的时候, 尽可能将有可能被一起访问, 因为这样能够保证cpu 从内存读取数据的时候都在一个 cache line 里面, 并且cpu 默认会进行预取。读写的字段也可以分开安排,让这部分的数据在不同的cache line 中。
  3. 做好一个struct 内部的padding, 也就是尽可能按照64 bytes 做padding, 这样可以避免的问题是一个struct 在64 btyes 之内的不同的变量如果被多个cpu 获得, 也可能跨cache line, 那么在修改其中一个的时候, 需要把另外一个invalidate 掉才行. 特别是多核的场景, 这个问题就很明显.但是这里也有一个折衷, 就是我们设计struct 的结构体应该是尽可能的紧凑, 这样才能更有效的利用cpu cache line. 但是这里增加了padding 以后, 这个struct 就不紧凑了, 那么怎么权衡呢?在读多写少的场景中, 是不用在意 cache 冲突, 更在意的是内存的紧凑, 或者是局部性. 因为很少发生多个核修改同一个变量的场景, 通过padding 的设计反而需要cpu 进行两次读取.但是在写多读少的场景中, 就更多的需要注意padding, 因为多核场景大量的写入很容易导致cache invalidate, 因此需要更注重padding。
  4. 尽可能的只让一个cpu 去访问某一个变量的内存, 这样cache line 被失效的情况就少很多, 代码里面尽可能减少全局变量
  5. 类似于cpu 的体系结构那样, 做batch, pipeline.

4. Memory

正确理解Memory Ordering与并发的关系

4.1 Atomic

涉及到内存的读写指令,特别是非对齐的双字操作,可以使用专用指令:CMPXCHG, XCHG, XADD等,或使用lock指令(使用在指令之前表示当前指令操作的内存只被当前CPU所用)

4.2 Memory Barrier

待补充

4.3 程序优化

未完待续!持续更新

参考

  1. CPU架构浅析
  2. Memory Barriers: a Hardware View for Software Hackers
  3. 关于CPU Cache — 程序猿需要知道的那些事
  4. Gallery of Processor Cache Effects
打赏作者
提交看法

抢沙发

还没有评论,你可以来抢沙发