Moore定理
The number of transistors that can be inexpensively placed on an integrated circuit is increasing exponentially.
Amdahl定理
Performance decreases as number of processors increases once there is even a small percentage of nonparallelizable code.
摩尔定律告诉我们芯片的处理能力呈指数级上升,阿姆达尔定律告诉我们由于存在不可并行的代码,加速比存在门限。福音越来越遥远,现实越来越残酷。现在使用的海量数据处理系统,都试图通过软件来解决扩展性、容错性、并发性等分布式系统面临的问题。
在介绍并行处理系统时,需要注意并行编程不等于并行处理(Concurrent program != Parallel computing)。 线程主要用来表示异步,而不是并发。随着多核技术的出现,并发才变得越来越重要。
最常见的并行系统是:基于共享状态的并行系统。经常使用的多线程技术,进程间通信(IPC)就属于此类,与此相关的共享内存映射也属于此类。由于缺少语言层面的支持,码侬必须自由非常谨慎地处理共享状态。但共享状态有下面的武器杀死你所有的脑细胞:
1. 不可决断,状态一直超越预期
2. 原子性
3. 死锁(简单解锁方法,顺序申请资源)
4. 活锁(某弱势一方一直得不到资源,解决方法排队,先来先服务)
5. 公平性/饿死
6. 竞争条件
还存在三种对码侬友好的并行处理模式:
1. 软事务(Software Transactional Memory,STM)
2. Message-Passing Concurrency (Actors)
3. Dataflow Concurrency
STM将内存堆或栈看作事务性数据集,类似于数据数据库,可以开始一个事务,提交一个事务,甚至中止回滚事务。在遇到冲突时,事务自动重试。遇到中止时,内存回滚。事务是可嵌套的。
atomic {
..
atomic {
..
}
}
STM有一些限制:属于同一个事务的所有操作必须是幂等(Idempotence)的,并且没有任何副作用的。
(什么是幂等呢?对于单目运算,如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次该运算所得的结果是一样的,那么我们就称该运算是幂等的。对于双目运算,则要求当参与运算的两个值是等值的情况下,如果满足运算结果与参与运算的两个值相等,则称该运算幂等。HTTP是一个典型的具有幂等性质的应用,访问一次站点和多次是一样的。银行结算也是幂等的,用户肯定不愿意点两次账单就算两次钱)
Carl Hewitt在1973提出了Actor。Actor具有如下特点:
1. 没有共享状态
2. 轻量级进程(通常是线程实现,共存的线程数量可以很大,测试表明在4G RAM上可以创建6.5 million)
3. 异步消息传递
4. 邮箱缓存消息
5. 通过模式匹配接收消息
Dataflow具有内在的安全性、鲁棒性和并行性。Dataflow中,操作又一系列预先定义好输入和输出的黑盒完成。与传统编程方式不同的是不再需要用语句指明做什么。想流水线的工人一样,每个工人快速完成自己的任务然后传给下一个工人,工人之间彼此独立完成不同的工作。Dataflow的操作也不需要状态。
Dataflow运行你在多个执行实体之间共享变量,因为它只允许对一个Dataflow变量写一次,对Dataflow的读的次数不做限制。可以看出基于Dataflow的并发是幂等的。
Dataflow并发的优点在于:
1. 没有资源竞争
2. 没有活锁
3. 确定性死锁
4. 完全确定的编程方式(知道程序执行的流程细节)
有没有一种语言能够支持所有的四种并行处理模式呢?语言提供内置支持的,没有。但Groovy,Scala和Java都有相关的类库提供支持。GPars(Groovy Parallel Systems,http://www.gpars.org/)是一个很好的并行类库。其目的是提供为编写并行程序提供多种高层抽象,包括MapReduce、Fork/Join、异步闭包、Actor、Agent、Dataflow等,并且简化并行代码的编写。
GPars 的构思源自其他语言的一些最受欢迎的并发性和协调模型,包括:
1. Java 语言的 executors 和 fork/join
2. Erlang 和 Scala 的 actors
3. Clojure 的 agents
4. Oz 的数据流变量
Groovy 和 GPars 的结合成为展示各种并发性方法的理想之选。
Akka是一个提供给Scala的Actor模型,旨在简化编写高容错、搞可靠性系统。
/*Groovy、Scala和Java都是JVM语言,可以从侧面反映出JVM已经是一个被广泛接受的概念。回顾一下,IOC、XML DOM SAX这些概念也是Java出现而逐渐兴趣的。我们已经到了JVM时代。再直接一点,我们现在是虚拟化时代。*/
并行计算的理想境地应该是:
1. 函数式编程,函数调用没有任何副作用,采用不可变(immutable)数据;
2. 没有共享,包括状态和数据,分布式计算跟本地计算一样;
3. 通过消息传递实现实体间通信。
/*数学理论上的并行系统模型:
1. Actor Model
2. Process calculus(http://en.wikipedia.org/wiki/Process_calculus)
*/
Gpars文档中将Gpars实现的并发机制分为三类:
1. Code-level helpers Constructs that can be applied to small parts of the code-base such as individual algorithms or data structures without any major changes in the overall project architecture
· Parallel Collections
· Asynchronous Processing
· Fork/Join (Divide/Conquer)
2. Architecture-level concepts Constructs that need to be taken into account when designing the project structure
· Actors
· Communicating Sequential Processes (CSP)
· Dataflow
· Data Parallelism
3. Shared Mutable State Protection Although about 95% of current use of shared mutable state can be avoided using proper abstractions, good abstractions are still necessary for the remaining 5% use cases, when shared mutable state cannot be avoided
· Agents
· Software Transactional Memory (not fully implemented in GPars as yet)
实现并行化首先应该从架构角度考虑,然后才是在代码实现,通过抽象可以解决大部分的共享状态带来的状态保护问题。