课程笔记:Parallel Programming in Java(第三、四周)

Parallel Programming in Java(第三、四周)

Parallel Programming in Java 是 Coursera 的上的一门课程,一共有四周课程内容,讲述Java中的并行程序设计。这里是第三、四周课程的内容笔记。主要内容为 Parallel LoopsDataflow Synchronization and Pipelining,即 循环并行数据流同步及流水线

Parallel Loops

循环结构在编程实践中是一类很常见的构建模型。程序中的循环结构主要可分为:

  • pointer-chasing loop

    for(p = HEAD; p != NULL; p.NEXT) {
    	compute(p);
    }
    

    这类循环结构中,因为每次循环处理的指针对象都是相互独立的,因此任务完全可以分开进行处理。可以简单地采用将循环看做 async task 使得计算模型并行化

    FINISH{
    	for(p = HEAD; p != NULL: p = p.NEXT) {
    		ASYNC
    		compute(p);
    	}
    }
    
  • Iteration loop

    for (i : [0 : N-1]) {
    	A[i] = B[i] + C[i];
    }
    

    这类模型和pointer-chasing loop最大的不同处在于这类循环中,循环的次数是可以提前知道的。

    • 简单的使用forall代替for来调用现有的API就可以自动的并行化。

    • 或者调用 stream 来并行化 for 循环。

      a = IntStream.rangeClosed(0, N-1).parallel().toArray(i -> b[i] + c[i]);
      

      但具有多个返回值的时候还是使用forall构建会更清晰简单。

矩阵乘法的并行化

一个串行化得矩阵乘法实例

for([i, j] : [0:N-1, 0:N-1]) {
	c[i][i] = 0;
	for(k : [0:N-1]) {
		c[i][j] += A[i][k] * B[k][j]
	}
}

并行化时,将外层循环的 for 替换为使用 forall 。内层的 k 循环是不能进行进一步的并行化的,因为在计算的过程中如果进行并行化就会产生数据竞争。

循环并行的屏障(同步)

在一个并行循环的模型中,循环体部分可以加入 barrier。作用是进行进程之间的同步,进行第一次执行到 barrier 会等待,待所有线程都到达之后再一次开始。通过加入 barriers,for的循环体被分割为不同的 phases 进行操作,进程会在 phase 之间进行同步,然后继续并行执行。

注意事项

使用 forall 循环时,将全部循环都创建 task 有时并不是好的方案,应当根据具体的硬件环境(即处理器核心等)创建适合的并行模式,在尽量完全的利用硬件运算优势的前提下减少因为 task 分配造成的开销。

例如对于计算向量和的程序并行化,将整个向量分块会是一个较好的方法,常用的分块方法有两种:

  • 使用固定大小的block进行分块
  • 使用 cyclic 模式进行分块,即 i  mod  N G i\text{ mod } NG i mod NG 的结果作为分块依据(这通常适用于向量任务中计算量分配不均衡的时候,使用这种方法能够最大程度上地平衡 workload)

Dataflow Synchronization and Pipelining

Split-phase Barriers with Java Phasers

在一般的使用 barrier 的任务中,通常同步操作本身是需要消耗开销的。实际上,这部分开销是可以整合在程序的其他部分中的,即将 barrier 放置在程序的某个步骤中,在执行的同时进行同步。

在 barrier 执行时,实际上分为几个步骤,即 ARRIVE-AWAIT-ADVANCE。因此,显然将这些步骤分离,将不是必须等待的步骤提前执行,让程序拥有更多的“等待缓冲”时间是一个较好的优化思路。phaser object 可以分为两部分:ARRIVE 和 AWAIT-ADVANCE。通常的 barrier 模型可以表示为

forall(i : [1:N]) {
	print("HELLO");
  myid = LOOKUP(i);
  NEXT;
  print("bye" + myid);
}

使用 phaser 之后,模型可以表示为:

// initialize phaser ph	for use by n tasks ("parties") 
Phaser ph = new Phaser(n);
// Create forall loop with n iterations that operate on ph 
forall (i : [0:n-1]) {
  print HELLO, i;
  int phase = ph.arrive();
  
  myId = lookup(i); // convert int to a string

  ph.awaitAdvance(phase);
  print BYE, myId;
}

ARRIVE 表示当前线程进入了这个 phaser ,但并不需要等待,仍然可以继续执行一些本地化的操作。执行到 AWAIT ADVANCE 时才是真正需要等待同步的地方。因此,使用 phaser 相当于将同步操作和本身需要执行的步骤进行了一定程度的重叠,使关键路径时间缩短。

Point-to-Point Synchronization with Phasers

使用 phase 的同步模型可以使得当数据依赖关系复杂时,保证在效率最高的情况下没有数据竞争地完成任务。以下面的计算依赖关系为例:

Task 0Task 1Task 2
1a:X=A();//cost=11b:Y=B();//cost=21c:Z=C();//cost=3
2a:ph0.arrive();2b:ph1.arrive();2c:ph2.arrive();
3a:ph1.awaitAdvance(0);3b:ph0.awaitAdvance(0);3c:ph1.awaitAdvance(0);
4a:D(X,Y);//cost=34b:ph2.awaitAdvance(0);4c:F(Y,Z);//cost=1
5b:E(X,Y,Z);//cost=2

上面的任务中,如果不使用 phase 模型,就必须等所有 task 的第一步完成之后进行一次同步,再共同开启后面的程序。但使用 phase 模型可以让任务更加精确地知道自己在等待的任务,一旦该任务完成便可以立即开始。

Pipeline Parallelism

即流水线并行,适用于可分为多个独立步骤并且需要处理序列化的多个独立输入的任务。

在实现时,每个步骤之间只需要等待前一个步骤完成就可以执行。使用流水线模型,假设要处理n个输入,每次处理需要p步,时间都是1,则 W O R K = n × p , C P L = n + p − 1 , P A R = W O R K C P L = n p n + p − 1 WORK=n\times{p}, CPL=n+p−1, PAR=\frac{WORK}{CPL}=\frac{np}{n + p − 1} WORK=n×p,CPL=n+p1,PAR=CPLWORK=n+p1np ,当 n 远大于 p 时,效率趋近于 p,这已经达到了最理想的情况了。

Data Flow Parallelism

数据流并行模型意在使用 async 来实现并行计算图的构建,从而更加具体地体现并使用数据操作之间的依赖关系。假设现有的数据依赖为:A → C, A → D, B → D, B → E,则可以构建以下模型:

async( () -> {/* Task A */; A.put(); } ); // Complete task and trigger event A
async( () -> {/* Task B */; B.put(); } ); // Complete task and trigger event B
asyncAwait(A, () -> {/* Task C */} );	    // Only execute task after event A is triggered 
asyncAwait(A, B, () -> {/* Task D */} );	  // Only execute task after events A, B are triggered 
asyncAwait(B, () -> {/* Task E */} );	    // Only execute task after event B is triggered
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C语言并行编程:MPI和OpenMP解决方案手册》是一本关于并行编程的参考书籍。在这本书中,作者详细介绍了如何使用MPI和OpenMP这两种并行编程模型来编写C语言程序。 MPI是一种消息传递接口,它允许在分布式内存系统中进行并行计算。它允许程序在多个处理器之间传递消息,并协调它们的计算工作。本书通过一系列具体的示例,教会读者如何使用MPI来设计和实现高效的并行算法。 同时,本书还介绍了OpenMP并行编程模型,它允许程序员通过将其代码中的特定区域标记为并行区域来实现并行化。这些并行区域可以在多个处理器上同时执行,从而大大提高程序的性能。作者详细讲解了如何使用OpenMP指令来标记并行区域,并提供了一些实用的技巧和技术来充分利用多核处理器的能力。 《C语言并行编程:MPI和OpenMP解决方案手册》还涵盖了一些并行算法和并行编程的最佳实践。作者通过一系列的练习和示例,帮助读者掌握并行编程的核心概念和技巧。此外,书中还提供了一些常见问题的解答和调试技巧,以帮助读者克服在并行编程过程中可能遇到的困难和挑战。 总之,这本书提供了一个全面而简洁的指南,帮助读者理解并行编程的基本原理,并掌握使用MPI和OpenMP来实现高效并行算法的技能。无论是学生、教师还是专业开发人员,都能从这本书中获得实用的知识和技巧,使他们能够更好地利用并行计算的潜力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值