您的位置:首页 > 编程语言

关于并行处理的一些思考

2014-02-11 11:17 330 查看
我一直以来对于分布式计算都非常有兴趣(主要原因也许是我们家电脑太渣了跑不动很多游戏。。。),于是乎,我最近就粗略的了解了一下这方面的一些内容。
我首先从一般的分布式存储及运算架构开始了解,我个人觉得这些经典的架构,包括mapreduce,做得是一种特定情况下的并行化处理。也就是说,我们程序员必须知道具体问题的并行算法。但是,我觉得应该有更一般的并行框架可以自动的进行算法的并行化,而不是需要显示的进行并行申明。我看了一些经典的文章介绍现在比较流行的并行化处理机制。也许是我积累不够,才疏学浅,当时并没能找到关于通用任务自动化负载平衡算法。。。
于是乎,我就在思考,如何去实现这样一个自动的并行化算法呢。这时候我将自己的目光从大的以网络为基础的分布式计算,缩小到多核CPU的负载均衡问题。(因为这样的话我们就可以不去考虑通信的不可靠性,延迟以及数据损坏的问题。)我们都知道现在多核CPU是由操作系统负责以线程为单位的进行尽可能的并行处理。但是如果我们面对的是一个进程(例如一个大的游戏的物理运算模块),那操作系统也就没有什么办法了。所以,我觉得我们应该从编译前入手,对于源代码进行操作(例如分割成多进程),来达到并行的目的。
那我们可以做得有两个方面,一个是编译器在编译时对于代码进行分析并分割,第二个就是给予程序员直接分割和操作进程的能力。
对于第一点,研究一直在进行(大家可以去查查一些针对多核的编译器的优化),我查到了intel已经有了一个有自动并行优化能力的编译器。
而对于第二点,我查到了很多针对Java的进程并行操作方法,我不是很了解,也就不再多说。
但是,网上有很多大牛们对于自动化并行编译器不是非常看好,因为有一些算法是无法在不改变逻辑的情况下进行分治。说简单点,就是算法流程逻辑是线性思维,他们的程序肯定是串行的,这是无法进行优化的。顶多将一些没有依赖关系的循环拆分开来,但做到这一点在很多情况下也许要编程者的显示申明,因为编译器对于一些语句的依赖关系也无法判断。
(例如如下的例子:

for i:=1 to 1000
a[i]:=a[i+k];


如果k〉1000则编译器完全可以将这个循环分拆,但是编译器在没有运行这个程序的时候,是无法知道k是否大于1000。)
这时候能就有大神提到了函数式编程(FP),因为在函数式编程中强调每个函数的无副作用性,所以就拥有了很大的并行化潜力。
我个人认为函数式编程的主要有点在于,语言本身要求编程者将依赖关系在抽象时理清楚,并且用返回值得形式(纯计算)而非改变状态的方式进行输出。我以前并没有接触过FP,所以借着这个机会把Lisp好好研究了一下。(lisp真是博大精深。。。里面所蕴含的思想远远不止函数式编程,还包括了元编程的很多核心思想,也许我后面会对于lisp的一些内容总结一下)大家都知道lisp是一族方言的总称,现在比较流行的有scheme和common lisp(我估计大家接触lisp基本上都是因为SICP的缘故吧,我现在还在啃。。。)可惜的是这两个大的分支没有很好的支持并行化处理。但是有一些方言,例如multilisp,很有建设性的针对并行化引入了一些概念,下面我们来看一下。
(以下摘自wikipedia)
      "MultiLisp achieves parallelism with thePCALL macro,where (PCALL FunA B C ...) isequivalent to (Fun A B C ...) except that
      the arguments A, B, C, etc. areexplicitly allowed to be evaluated in parallel; this circumvents the usualorder of                 evaluation,which is sequential and left to right. It also makes use of a parallelprogramming construct called futures,
which      resembles forking,combined with Lazy evaluation. Usingthis construct, an expression such as
      (cons (FUTUREA) (FUTURE B))
      can be written, which will overlap the evaluation ofthe expressions A and B, not only with each other, but with computations      that use the result of the cons call,until an operation is performed that
requires actual information about thevalue of A or       B."("MultiLisp.")
总而言之,这里的重点是A和B的表达式的求值与后面的运算没有关系,所以可以交给空闲的CPU去做并且主进程继续做下去。这个概念很简单明了,但是无法处理一个情况:表达式A是分支语句的判断条件。
例如如下代码:

(if (< (setf A (+ 1 2)) 9)
(+ 2 3)
(- 2 3))


无论如何所有的CPU都得等A出结果以后才能继续执行下去。但是我们可以发现在分支后的语句中在一些情况下,是跟A表达式没有什么关系或是可以lazy evaluation跟A有关的表达式。换句话说,其他CPU完全可以继续往下执行,如果知道是选择哪个分支的话。但是为何我们不让CPU把所有分支都走呢?也就是说,在计算A表达式的时候,其他空闲CPU可以并行的处理所有的分支,当主CPU得到表达式的结果后,正确分支的CPU可以做为主CPU继续计算下去,这样就可以做到了前面那段运算时间的节省,毕竟其他CPU闲着也是闲着,不如干点事,如果干的是没用也没有关系,因为机会成本为0。

Thank you for reading my blog.
 
 
Work cited
"MultiLisp." Wikipedia. Wikimedia Foundation, 15 Jan. 2014.Web. 10 Feb. 2014. 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息