系统中国

已搬家至http://www.davelv.net

关于并行计算发展的一些思考

本文是在CU论坛上回答网友的问题《C语言标准有没有规定前一个语句必须在后一个语句之前完成?》而引发的关于并行处理的一些见解和思考,以下正文

  难得OwnWaterloo老兄想起了在下,以及最近我也看到了一些并行方面的帖,就浮上水面说说自己的见解吧。
  C语言是典型的Von Neumann体系下的编译性语言:"存储程序并顺序执行"。
  70年代初的时候C语言发明和Unix的产生是息息相关的,必须要在一起说明的。当是计算机并行运算并不是很普遍,绝大多数的并行是靠多进程完全任务的,线程还没有被广泛提及,CPU也没有乱序执行的概念。这时候进程间的并行就靠操作系统去隔离,这一点当是的Unix做的非常不错,并由于涉及到Unix设计哲学(简单即美)复杂而高效的多线程在Unix类系统上一直没有得到重视,这也是影响了C语言的产生和发展。C语言设计者也认为C语言应该足够小巧灵活(C语言以控制和处理内部(存)数据为优先任务,个人见解),不要引入太多平台差异的东西,于是I/O功能和后来突飞猛进的并行计算自然只能丢到库中或者干脆完全隔离不予考虑。
  计算模型中的多进程内存屏障靠OS就可以,主要就是一些进程通知和I/O共享的问题,得益于CPU对多任务的支持和I/O本身的串行性和独立性等,这些并不是很难解决。但并不是所有的并行问题完全可以靠OS来完成。多进程读写共享文件,这种情况在70年代的时候也没有现成的函数使用(现在有文件锁这种东西),只能自己用OS提供的信号机制而自己设计模型。不过由于这样的问题已经比较依赖业务逻辑,所以交给程序员自己去处理也是符合C语言和OS的设计特性,而且Unix也做了很多细节屏蔽,对程序员是足够友好了。这里典型OS代表是1976年的Unix 6th(这是从贝尔实验室流出并广泛使用的Unix版本)。
  多进程间用I/O和信号去传送数据有时候不能满足需求,于是Unix又引入了共享存储和消息队列的概念和相关操作。但这也增加了操作复杂性,共享存储的互斥需要程序员手工去完成(一般是和I/O互斥一样用信号通信),而且虽然一次传送的数据多了,过于频繁的互斥操作仍受到信号机制的限制。典型OS代表是1983由AT&T于年开始发行的System V系列版本。
  然而随着计算机科学发展,计算机所处理的任务复杂度也是越来越高,这也必然要求软件设计的复杂度增高。多进程看起来不能满足所有的并行问题,于是多线程概念被提及和重视起来。这样就引来的新的复杂机制,就是进程内的共享问题。C语言仍遵循着设计理念,这些交给程序员和OS处理,语言本身不参与处理这些问题。于是Unix又修改了信号和进程部分等,加入了线程模型。由于进程内部出现了临界区,以及线程和信号的相互作用,使得并行计算模型大幅度复杂化。而且由于Unix世界的两次大战,各个分裂的众支持者们对多线程的模型理解不一,使的各个版本多线程差异很大,直到1995年POSIX 1.c标准引入了线程模型才有一个统一的标准。前几天在群里看了一个图片。从单进程,到多进程协作,再到多线程,熟练掌握这些技术的程序员数量急剧递减,也说明了一个问题?我们的计算模型是不是太复杂了?
  上面谈论基本上是OS和并行模型的问题,内容似乎太多了。下面再说下CPU的问题。CPU的并行计算模型也是和OS一样进化的,也是有了CPU支持OS才能更好的处理这些问题。多进程和多线程的支持不再细说,就说咱们议论的重点,乱序执行。乱序执行的概念早在上世纪60年代提出来的,但是直到90年代初IBM才制造出实用的微处理器POWER1,然后后来的intel,sun各家也开始使用这项技术用于CPU制造以及编译器优化,到现在也不过20年和多线程技术的实用年份差不多。总体来说这并不像多进程和多线程一样是架构上的并行技术,而是一种局部优化技术。连多进程和多线程都没有对C语言本身产生明显影响,自然乱序执行也不会有这么大能力。
  不过一个事物既然产生了,它的影响还是有的,虽然它仍然在宏观上维持着Von Neumann的计算体系,但在微观上却背离了。拿编译器为例,有些指令或者语句会被优化成更高效率的序列,当然前提是在不影响程序执行的情况下。如果影响了程序的行为,那么我们可以说这是编译器的一个BUG。关于这点问题zylthinking网友在帖子《为什么用户空间程序不用考虑内存屏障?》提到了,我的回答也是显然易见的,看语言规定的计算模型而论。C语言自然规定了语句的顺序执行,所以编译器也得按照顺序执行的行为来处理,如果有任何乱序影响了最终结果,那么负责任的应该是编译器,而不是使用编译器的程序员(谁来负责这只是理想的说法)。但是可不可以发明一种在计算模型上就支持乱序执行的语言呢?我目前的答案是否定的,因为程序设计语言的计算模型本来就是把细节抽象掉,更多去关注高级逻辑。对于这样一种宏观和微观不一致的优化手段,我们没有必要花费太大的功夫在它身上,这是得不偿失的,用不同的抽象层次去屏蔽这些不一致即可。这样只需要编译器和CPU设计者对乱序执行进行良好的处理,就可以保证绝大多数开发者在不知情的情况下达到程序加速的目的。
  虽然乱序执行这种技术不能从根本上撼动我们传统的计算模型,但它是一个种子,联合起来起多进程和多线程的概念,以及以后出现的并行计算技术会不会导致我们产生颠覆性的计算模型和计算语言呢?这点还是要归到抽象层次上。因为计算机是为人类服务的,而人能驾驭的复杂度是有限的。就像计算机和计算机网络的产生和发展改变了人类信息存储,处理和交流的方式。但是人类不会用C语言去进行日常交流,C语言只是给程序员用的,程序员也不用完全掌握电子信号怎么在网络间传播也可以写出正确的通信程序。同理以后会有更多更新的计算技术。但是这些计算技术的细节会被层层分解和屏蔽,使得人类可以在自己能掌握的复杂度内解决问题。首先界定问题,才能处理问题。但是整体问题还是会变得复杂,你若想深入了解问题,自然要学习的东西会更多。
  说了这么多东西,其实都是行而上泛泛之论,没有说到具体细节。至于C语言标准有没有规定前一个语句必须在后一个语句之前完成?这个问题,答案自然就是肯定的了。

  再次给予DMR崇高的怀念和无上敬意

附相关链接:

《C语言标准有没有规定前一个语句必须在后一个语句之前完成?》http://bbs.chinaunix.net/thread-3608228-1-1.html

《为什么用户空间程序不用考虑内存屏障?》http://bbs.chinaunix.net/thread-3607285-1-1.html

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭