不变的道——多核、并行程序设计、网格计算、超级计算机、云计算
不变的道——多核、并行程序设计、网格计算、超级计算机、云计算
前面讲多器件之间的配合连接,现在以计算为主谈并行程序设计中的要点。
一、两种隐藏的并行
很长时间以来,程序员免费享受了由硬件和操作系统的改进所带来好处。这些性能提高包括器件集成度的提高以及隐藏的并行——指令级并行和任务级并行。这些改进的细节对于程序员来说一直是可以忽略的,程序设计和单处理器时代一样。
指令级并行和任务级并行是两种相对隐藏的并行。程序员可以完全不理会这两种并行性,像单处理器一样编程就可以享受性能上的改善。但是也并不是完全无事可做,针对指令级并行的代码优化可以由编译器很好的完成,在程序员编写代码的时候也可以有意的利用指令级并行,减小相邻指令间的相关性。
相对来说,操作系统中的任务级并行是隐藏的不够深的一种并行,不需要很深的编程经验就可以利用。chrome浏览器的多进程模型就可以很好的挖掘现在正大量普及的多核处理器。但是多进程模型带来了进程间的通信问题,解决好多进程间的通信要比程序内通信要困难的多。
二、挖掘多核的性能
虽然程序员可以免费的享受上面两种隐藏的并行所带来的性能提高,但是想要写出性能优异的程序,还是有必要进行分布式的程序设计。所谓分布式处理,就是把原有的一个大任务分解,由不同的处理单元(CPU或者PC)分别处理,最后汇总。但是任务并不一定容易分解,当下一次操作用到前一次操作的结果的时候,这两次操作就存在关联性,无法并行完成。现在举一个容易分解的任务为例:统计一个大的数组中的特征值的数目。
方案一:用单核处理器的做法是对数组中的所有值逐个判断,并用一个变量统计特征值的数目。这样设计的程序在多核处理器下只会占用一个核,其它核则处于闲置状态。
方案二:在多处理器或者多核环境下,可以把数组分组,用多个线程分别统计。每个线程会由操作系统自动调度到不同的核执行,或者也可以由程序绑定到不同的核执行。这种模型下,每个线程使用同一个统计变量,并互斥的访问这个共有变量以保证运行结果的正确。虽然这种程序使用了分布式程序设计,但是由于大量的互斥在存在,程序运行的效率将并不高。如果特征值的匹配较快,这种大量的互斥反而会增加总的时间开销。
方案三:为了降低线程间互斥性(耦合性),每个线程用单独的变量进行统计,线程统计完成后再把统计结果汇总。这样可以减小互斥,但是其中还有一个讲究——cache。由于CPU的cache是以64字节为单位的。以下图,i7处理器架构,为例。i7处理器是四核模拟八线程处理器,每个核有一个一级cache,每两个核共享一个二级cache,所有核共享一个三级cache。当我们用四个线程进行处理,生成四个int型变量分别统计。由于四个int型占用16字节空间,事实上还是在同一片cache中。当其中一个线程改变它的统计变量,CPU内部为了维持cache的一致性,就要进行一次和其它核的通信。所以核间的通信开销仍然是非常大的。为了解决它,就要保证四个统计变量分别在不同的cache中:在分配变量的时候,在每个变量后预留60字节,组成一个cache大小。
那么,总结一下分布式处理的设计:
- 把任务分解,找到任务分解的方法。并尽量平均分配任务量。或许还要用到一些统筹规划的方法。
- 分解后的每个子任务可视为一个独立的执行单元,或者一个元素。元素间必然存在通信问题,要提前考虑并解决。
- 降低任务间的耦合性,可有效减少通信开销。
- 传统的程序设计中,用到全局变量、动态内存和函数参数进行数据传递。在分布式程序设计中,这些数据传递方式可能给硬件带来隐藏的通信开销。
三、简化分布式计算模型
上面的例子给出了多核编程和单核程序设计下的差异性,主要不同有:程序员需要考虑如何将任务分解;程序员要设计各处理单元间的通信问题;解决并降低处理单元之间的耦合性;为保持数据一致性导致的额外通信。
这些任务目前无法由编译器全部代劳。为什么并行程序设计有这么大的挑战性?DAVID PATTERSON在The Trouble With Multicore一文中总结并行程序设计的三大困难:负载均衡、依赖性、同步(load balancing, sequential dependencies, and synchronization)。并列举了在并行化道路上,采取的方法:
- 开发并行编程的语言(Languages for parallel programming,成百上千种努力,但是都以失败告终)
- 硬件解决(New computer architecture) 。开发并行计算的计算机,但是无一成功。
- Software that will automatically parallelized existing programs(一定程度上成功,效果不理想)。可用在消息处理,多进程调度等,存在自然的并行性的地方,在多核处理器下效果理想。
我认为文中所列的思想过于极端了——用原有的顺序程序的设计方法去设计程序,由机器自动完成并行化。倒不如从一开始就认识到并行的存在,然后再去简化和服务程序设计。看IT架构扩容——如何进行存储整合一文中所说:
用户很难将一个很大的应用,比如需要16核物理服务器的应用,运行在两台8核物理服务器上,而云存储则可以将一个需要大容量存储的应用,分布在多台存储上实现,而且可以获取更高的性能。
这里点出了一个很核心的问题,把计算分散和合并要比把数据分散和合并难的多。但是既然是一个运行在16核服务器上的应用,那么并行化就已经是做好了的,可以想像有16个线程或进程在运行。那么为什么在同一台物理机上的16个线程或进程想要移到两台物理机上会那么难呢?
- 线程间无需特别的手段进行通信,因为线程间可以共享数据结构,也就是一个全局变量可以被两个线程同时使用。不过要注意的是线程间需要做好同步。同一个进程内的线程想要完成通信,只要互斥的访问全局变量即可。但是移到两台物理机上或者同一台物理机的两个不同进程后,数据同步的开销和复杂度都有很大提高。
- 一台物理机内的进程间通信,和运行于两台物理机的两个进程间通信方式不一样。代码需要进行修改。
所以如果能把不同的数据同步过程隐藏到后台,并把不同的通信方式进行统一封装。这种计算的“分”与“合”就会变的容易。我曾经把进程间的通信和网络通信封到一起,使用同样的函数,程序设计者可以完全忽略通信对方是在本机还是在远程主机。如果在此基础上把线程间的数据交换也统一起来,并把线程和进程的迁移做好。程序设计者眼里就只有处理单元和处理单元间的通信。系统自动选择较快的通信方式,把通信较多的两个处理单元迁移到一台PC,甚至一个进程内。
四、超级计算机
在2010年国际TOP500组织公布的最新全球超级计算机500强排行榜上,中国首台千万亿次超级计算机—“天河一号”位居第一。
超级计算机中的基本问题
是不是装上更多的CPU,就能获得更高的运算性能。这实际上是一个巨大的误解,除了处理器以外,现代超级计算机还涉及到并行访问档案系统、资料分区、非均匀访存模型、冷却等多种“软”“硬”技术。如果综合起来,超级计算机的核心技术是“架构”问题。几万个处理器,怎样才能够充分发挥他们的一起工作的性能,并不是向“并联”电路这么简单。而且这个架构还应该有良好的扩充性。(from: 凤凰军事)
超级计算机的核心仍然是架构问题,即数据通路问题。
“天河一号”系统基本上采用CPU+GPU技术,但是芯片和互联通信都是采用世界通用技术。“天河一号A”系统我们采用了很多自主研发的技术。芯片还是英特尔的,不过我们使用了自主研发的飞腾1000,另外还有两千多个节点用的是我们自己的技术。整个技术的网络架构完全是由国防科大完成,而且这套互联通信比国际互联通信要快—互联通信可以看做是动车,芯片可以看做是铁轨,这两个部分同样重要。这么做的价值在于,美国核心技术有出口限制,对芯片的限制不大,但是对互联通信技术限制非常严格,“天河一号A”则在互联通信方面一下子赶超了美国。(from: 和讯科技)
这里强调互联通信,仍然是数据通路问题。
to be continued…