计算机组成 -- 超线程 + SIMD
超线程 – 线程级并行
Pentium 4
- Pentium 4失败的原因:CPU的流水线级数太深
- 超长的流水线,使得之前很多解决冒险、提升并发的方案都用不上
- 解决冒险、提升并发的方案,本质上是一种指令级并行的技术方案,即CPU希望在同一个时间,去并行执行两条指令
- 但这两条指令,原本在代码里是有先后顺序的
- 无论是流水线架构、分支预测以及乱序执行,还是超标量和超长指令字
- 都是想通过在同一时间执行两条指令,来提升CPU的吞吐率
- 但在Pentium 4上,上面这些方法都可能因为流水线太深,而起不到效果
- 更深的流水线意味着同时在流水线里面的指令就很多,相互的依赖关系就多
- 因此,很多时候不得不把流水线停顿下来,插入很多NOP操作,来解决这些依赖带来的冒险问题
超线程
- 无论是多个CPU核心运行不同的程序,还是单个CPU核心里切换运行不同线程的任务
- 在同一时间点上,一个物理的CPU核心只会运行一个线程的指令,其实并没有做到真正的指令级并行
- 超线程的CPU,把一个物理层面的CPU核心,伪装成两个逻辑层面的CPU核心
- 这个CPU会在硬件层面增加很多电路,使得可以在一个CPU核心内部,维护两个不同线程的指令的状态信息
- 在一个物理CPU核心内部,会有双份的PC寄存器、指令寄存器、条件码寄存器
- 在外面看来,似乎有两个逻辑层面的CPU在同时运行
- 因此,超线程技术也被叫为同时多线程(Simultaneous Multi-Threading,SMT)技术
- 但CPU的其它功能组件,没有提供双份,无论是指令译码器还是ALU,一个物理CPU核心仍然只有一份
- 因为超线程并不是真的去同时运行两个指令
- 超线程的目的:在线程A的指令在流水线停顿的时候,让线程B去执行指令,此时CPU的指令译码器和ALU是空闲的
- 线程B没有对线程A里面的指令有关联和依赖
- CPU通过很小的代价,就能实现同时运行多个线程的效果
- 只需要在CPU核心增加10%左右的逻辑功能,增加可以忽略不计的晶体管数量
- 超线程并没有增加功能单元(ALU),所以超线程只在特定的应用场景下效果比较好
- 一般是各个线程等待时间比较长的应用场景
- 例如需要应对很多请求的数据库应用,就比较适合使用超线程,各个指令都要等待访问内存数据,但并不需要做太多计算
SIMD – 指令级并行
- SIMD:Single Instruction Multiple Data,单指令多数据流,支持SIMD的指令集:MMX、SSE
- 两段代码
- 通过循环的方式,给list里面的每一个数加1
- 实现相同的功能,直接调用NumPy库的add方法
- 性能差异:32.72
- 原因:NumPy直接用了SIMD指令,能够并行进行向量的操作
- 通过循环来一步一步计算的算法,称为SISD,单指令单数据
- 如果是多核CPU,可以同时处理多个指令的方式称为MIMD,多指令多数据
- SIMD在获取数据和执行指令的时候,都做了并行
- 从内存读取数据的时候,SIMD一次性读取多个数据
- 下面程序数组里面的元素是integer,需要4Bytes的内存空间
- Intel在引入SSE指令集的时候,在CPU里添加了8个128Bits的寄存器
- 128Bits ≈ 16Bytes,即一个寄存器可以一次性加载4个整数
- 比循环分别读取4次对应的数据,能节省不少时间
- 在数据读取之后,到了指令的执行层面,SIMD也是可以并行执行的
- 4个整数各自加1,互相之间完全没有依赖,即不需要处理冒险问题
- 只要CPU里有足够的功能单元,能够同时进行这些计算,那这个加法就是4路同时并行的
- 因此那些在计算层面存在大量『数据并行』的计算中,使用SIMD能够很好地提升性能
- 实践:向量运算(同一向量的不同维度之间的计算是相互独立的)、矩阵运算
- 图片、视频、音频的处理
- 机器学习算法的计算
- 从内存读取数据的时候,SIMD一次性读取多个数据
- 基于SIMD的向量计算指令,是在Intel发布Pentium处理器的时候引入的指令集
- 当时的指令集叫作MMX(Matrix Math eXtensions,矩阵数学扩展)
- Pentium处理器,第一个有能力进行多媒体处理的CPU
1 | $ python |
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.