一直对多处理器结构一些基本概念和机制认知有些模糊,这阵子终于抽空看了《量化》的相关章节,清晰许多。画了张 脑图,总结了多处理器和缓存一致性的基本问题。
书里提到一个常见的认识误区,也是我平时容易忽视的:
用执行速度是否线性提高来衡量多处理器的性能
Measuring performance of multiprocessors by linear speedup versus execution time.
我们经常用同一段代码来比较不同系统的性能,但其实这是不公平的。因为并行版本的程序在单处理器上可能比串行版本要慢得多,相当于你让单处理器去做它不擅长的事情。公平的做法应该是比较各个处理器上各自最优的版本。但其实这样子则会带来算法差异的问题,因为并行版本往往采用针对并行优化过的算法,这样就是拿苹果和橙子比较了。为了区分这两种情况,作者提出相对加速比(relative speedup,用同样的程序测试出来的加速比)和真实加速比(true speedup,分别用各个处理器上最优的程序测试出来的加速比)这两个概念。
另一方面,作者说以是否线性提高来衡量多处理器 性能 也是不对;不过我觉得作者这一点多虑了。其实我们一般是拿线性提高来衡量系统的 伸缩性 ,这是没有问题的。但是单纯的看核数 / 处理器数的增加也是不公平的,比如肇国吴总他们当年测COREMU 时,就遇到过超线性加速的情况;这是由于当核数增多时,可用的 Cashe 也变大,提高了缓存命中率从而带来更好的性能。所以在评测中需要综合考虑 Cache,I/O 等子系统带来的影响。
比较两个处理器之间的加速比并不总能反映它们的相对性能。执行时间变化趋势图可以看,但是要小心别被误导。
另外 这篇文章 也总结了很多常见的评测和分析的误区,值得一读。
原子指令
还有以前傻傻的老搞不清楚为什么要用 exchange 这样的指令作为最基本的原子指令,为什么不实现一个锁呢?看书的时候顺便才想明白,其实我们拿锁的过程就是读取和写入锁状态两个动作,一读一写正好就是 exchange 的语义。由于硬件本身只能实现单独一个读或写操作的原子性,原子指令就是为了解决读写两个动作的原子性这个问题而引入的。
书上介绍的是用特殊的读指令 load linked(LL)和写指令 store conditional(SC)来实现原子操作。先用 LL 读取某个地址,在 SC 要写入值之前,会先检查 LL 所指向的内存值是否发生改变,是的话则操作失败。这样这一对指令合起来就能实现原子的 exchange 操作。书里没说 LL/SC 是怎么实现的,扫了 Intel 的文档也没看到相关介绍,我猜测应该是这样:LL 相当于注册了一个内存监听器,以后执行写操作时,会先检查内存地址是否被 LL 注册,是的话则置上某个 flag;当 SC 执行时先独占 bus,保证没有其他写操作进行,然后检查该 flag 是否被置上,是的话说明指定的内存被改过了,操作失败,反之则操作成功。
doubanclaim17b6d9ef1ce58f18