并行编程模型可以分为两类:(1)进程通信(process interaction)和问题分解(problem decomposition)。下面我们简要介绍这两类模型有哪些形式。
进程通信:进程通信涉及并行进程互相通信的机制。最常用的通信形式是共享内存(shared memory)和消息传递(message passing),但是通信形式也可以是隐式的,对程序员是不可见的。
1)共享内存:共享内存是进程间传递数据的一种高效方法。在共享内存模型中,并行进程共享一个进行异步读取的全局地址空间。异步并发访问可能导致条件竞争,因此需要同步机制来避免条件竞争,这些机制包括锁,信号量,管程(monitor)。传统的多核处理器是直接支持共享内存的,所以导致很多利用该特性的语言和库出现,如Cilk、OpenMP和Threading Building Blocks。
2)消息传递:在消息传递模型中,并行进程是通过消息传递来交换数据的。这些通信可以是异步的,即消息可以在接收者做好准备前发送,也可以是同步的,即只有接受者准备好接收消息时才能发送。消息传递的CSP(Communicating sequential processes)模型使用同步通信channel来连接进程,这种模式被Occam、Limbo和Go等语言所采用的。相反,Actor模型则使用异步消息传递。这种模式被D,Scala和SALSA等语言所采用。
3)隐式通信(Implicit interaction):在隐式通信中,进程通信对程序员来说是不可见的,进程通信是由编译器或者运行时来处理和实现。并发被预置在高级操作子中的领域特定语言(domain-specific language)和函数式编程语言是隐式并行的典型例子,因为无副作用(side-effect)允许非依赖的函数可以并发执行。但是这种并行模式是很难管理的。函数式语言如Concurrent Haskell和Concurrent ML提供了显示管理并行化的功能。
问题分解:并行程序是由同时运行的进程组成。问题分解涉及所有进程如何被组织起来的方式。问题分解包括三种并行模型:(1)任务并行模型(task parallelism);(2)数据并行模型(Data parallelism);(3)隐式并行模型(Implicit parallelism)。
1)任务并行化:任务并行模型关注进程或线程的执行。这些进程在行为上是不同的,而且相互之间的通信是非常重要的。任务并行化是表示消息传递通信的一种自然方式。在Flynn分类法中,任务并行化的三种形式是MIMD、MPMD或者MISD。
2)数据并行化:数据并行化关注在数据集上执行的操作。一组任务对数据集进行运算,但是会对不同的分区进行运算。在Flynn分类法中,任务并行化的三种形式是SIMD、SPMD或者SIMD。
3) 隐式并行化:对程序员来说是不可见的,由编译器、运行时或硬件负责实现。例如,在编译器领域,自动并行化就是将顺序执行的代码转换为并行代码的过程;在计算机体系架构领域,超标量执行就是一种利用指令级并行来实现并行运算的机制。
并行编程模型与并行计算模型是密切相关的。并行计算模型是用于分析计算进程代价的一种抽象,它不是必须具备可行性,即可以在硬件或软件上可以被高效地实现。相反,并行编程模型则明确地暗示了软硬件实现的可行性考虑。一种并行编程语言的实现可以基于一个或多个并行编程模型。例如,高性能Fortran就是基于共享内存通信和数据并行问题分解来实现的,Go语言则同时提供了共享内存和消息传递两种通信机制。