并行程序设计导论 第一章习题

写在前面——
感觉自己又在开坑了。前面《算法导论》和《多处理器编程的艺术》由于理论气息很重,最后题都没有做完。这两天也算是找到了这本《并行程序设计导论》,现在准备从最简单的并发的开始。
笔记应该不会做的太多,除非阅读到某些深有感触或有很大疑惑的地方才会写出来。
还有,对于CSDN博客支持markdown格式也是要赞一个的。第一次用markdown来写博客,很期待其效果。
多说无意,开坑吧。XD

习题1.11

1.1 为求全局总和例子中的my_first_i和my_last_i推导一个公式。需要注意的是:再循环中,应该给各个核分配数目大致相同的计算元素。

提示:先考虑n能被p整除的情况。

解答:
虽然,最后的提示说先考虑整除情况,但非整除的情况也不能不考虑。
这里我们先定义几个变量:
core_id∈[0, p),
num = n/p
rem = n%p

综合考虑写出下面的表达式:
my_first_i(core_id) = num * core_id
my_last_i(core_id) = num * (core_id + 1) + rem

1.2 我们已经隐含地假设每次调用Compute_next_value函数锁执行的工作量与其他次调用Compute_next_value函数执行的工作量大致相同。但是,如果当i=k时调用这个函数的时间是当i=0时调用这个函数所花时间的k+1被,即如果第一次(i=0)调用需要2毫秒,第二次(i=1)调用需要4毫秒,第三次(i=3)调用需要6毫秒,以此类推。那么对于问题1.1,你应该如何回答。

解答:
不知道有没有人对我之前错误的算法感兴趣。
下面,将使用微积分(数形结合)的方式来解这个题目。

首先,计算出每个核上平均的运行时间:
n个任务的总时间(串行) = [2n(n+1)]/2 = n(n+1) ms
因为有p个核,所以每个核上运行的平均时间 = [(n+1)n]/p ms

这里我们可以将任务时间的函数表达式写出来:
f(x) = 2x + 2 ,单位:ms
如果你有初中以上学历的话,你应该能用纸笔画出一条直线。
这条直线与x轴相交在(-1, 0)点,与y轴相交在(0, 2)点。
这里我们在x的正轴方向选取两个点,分别为(x1, 0), (x2, 0),且x1<x2。
然后,可以求出:
f(x1) = 2(x1)+2
f(x2) = 2(x2)+2

现在,求一下x正轴,y正轴,y=x1和任务时间函数直线所形成的面积。
面积是一个直角梯形,就用梯形的面积公式就好:
这里设为A = (2 + (f(x1))) * x1 / 2 = x1(x1 + 2)
同样的,与y=x2围成的面积,
这里设为B = x2(x2 + 2)
不过,这里B不是我们需要的,我们需要算出B中除A之外的面积。
所以,B’ = x2(x2+2) - x1(x1+2)

这里A的面积就相当于其中一个核上执行任务的总时间,B’的面积就箱单关于另一个核上执行任务的总时间。然后,根据题目要求,需要各个核上负载平衡,就可得:
A=B’ => x2(x2+2) - x1(x1+2) = x1(x1 + 2) => 2A = B

根据之前求得的平均时间,可以写出方程:
A=(x1+2)x1=[(n+1)n]/p
B’=x2(x2+2) - x1(x1+2)=[(n+1)n]p=>B=[2(n+1)n]/p

这里求解二元一次方程就很简单了,可以用一般解的表达式去套不同次幂的系数,也可以将方程配成完全平方公式进行求解。这里我就将函数式配成完全平方公式:
x1^2+2(x1)+1=[(n+1)n]/p+1
=>(x1 + 1)^2 = [(n+1)n]/p+1
=>x1={[(n+1)n]/p + 1}^(1/2) - 1

因为2A = B,所以x2={[2(n+1)n]/p + 1}^(1/2) - 1

同理可得,下一个三角形的面积为[3(n+1)n]/p,以此类推,
第k个三角形的面积是[(k)(n+1)n]/p

可得
my_first_i(core_id) = {(core_id + 1)[2(n+1)n]/p + 1}^(1/2) - 1
my_last_i(core_id) = {(core_id + 1)[2(n+1)n]/p + 1}^(1/2) - 2(为了不和下一个核上的第一个任务重合,这里减去了2)
这里暂不讨论小数情况了,如何对小数位进行处理可能会增加解答的长度。

1.3 尝试写出图1.1中的树形结构求全局总和的伪代码。假设核的数目是2的幂(1,2,4,8等)。提示:使用变量divisor来决定一个核应该是发送部分还是接受部分,divisor的初始值为2,并且每次迭代后增倍。使用变量core_difference来决定哪个核与当前核合作,它的初始值为1,并且每次迭代后增倍。例如,在第一次迭代中0%divisor = 0, 1%divisor = 1,所以0号核负责接受和,1号核负责发送。在第一次迭代中0+core_difference = 1, 1-core_difference=0,所以0号核与1号核在第一次迭代中合作。

解答:
这里使用C++的方式,对串行算法直接实现:
这里仅用书中例子作为测试例。

#include <iostream>
#include <math.h>

#define N 8

void foo(int S[]){
    int last;
    for (int divisor = 2, core_difference = 1; divisor <= N; divisor *= 2, core_difference *= 2){
        for (int first = 0; first < N; first += divisor){
            last = core_difference + first;
            S[first] += S[last];
        }
    }
}

int main(){
    //int S[n] = { 8, 19, 7, ... 13, 12, 14 };
    int S[N] = { 8, 19, 7, 15, 7, 13, 12, 14 };
    foo(S);
    for (int i = 0; i < N; i++){
        std::cout << S[i]<< std::endl;
    }
}

1.4 作为前面问题的另一种解法,可以使用C语言的位移操作来实现数形结构的求全局总和。为了了解它是如何工作的,写下核的二进制数编号是非常有帮助的,注意每个阶段互相合作的核。

从表中我们可以看到第一阶段,每个核预期二进制标号的最右位不同编号的核配对,在第二阶段与其二进制编号的最右第二位不同编号的核配对,第三阶段与其二进制编号的最右第三位不同编号的核配对。因此,如果在第一阶段有二进制掩码(bit mask)001(2)、第二阶段有010(2)、第三阶段有100(2),那么可以通过将编号中对应掩码中非零位置的二进制数取反来获得配对核的编号,也即通过异或操作或者^操作。
使用位异或操作或者左移操作编写伪代码来实现上述算法。

解答:
这里感觉文字描述并不是很清楚,所以还是笔算一下感觉才好。
用这里的函数直接替换掉上题中的函数即可。

void foo(int S[]){
    int mask_code = 1;
    int max_level = (int)std::log2(N);
    for (int level_num = 1; level_num <= max_level; level_num++){
        for (int i = 0; i != N; i += std::pow(2, level_num)){
            S[i] += S[i ^ mask_code];
        }
        mask_code <<= 1;
    }
}

1.5 如果核的数目不是2的幂(例如3、5、6、7),那么在习题1.3或者习题1.4中编写的伪代码还能运行吗?修改伪代码,使得在核数目未知的情况下仍能运行。

解答:
这里肯定可以运行,但是结果不正确。
1.3的代码做如下修改

void foo(int S[], int len){
    int last;
    if (len % 2){
        S[0] += S[len - 1];
        len--;
    }
    for (int divisor = 2, core_difference = 1; divisor <= len; divisor *= 2, core_difference *= 2){
        for (int first = 0; first < len; first += divisor){
            last = core_difference + first;
            if (last < len){
                S[first] += S[last];
            }
            else{
                S[0] += S[first];
            }
        }
    }
}

1.4的代码做如下修改:

void foo(int S[], int len){
    int mask_code = 1;
    if (len % 2){
        S[0] += S[len - 1];
        len--;
    }
    int max_level = (int)std::log2(len);
    for (int level_num = 1; level_num <= max_level; level_num++){
        for (int i = 0; i < len; i += std::pow(2, level_num)){
            if ((i ^ mask_code) < len){
                S[i] += S[i ^ mask_code];
            }
            else{
                S[0] += S[i];
            }
        }
        mask_code <<= 1;
    }
}

1.6 在下列情况中,推导公式求出0号核执行接受与加法操作的次数。
a. 最初的全局总和的伪代码。
b. 数形结构求全局总和。
制作一张表来比较这两种算法在总核数是2、4、8、…、1024时,0号核执行的接收与加法操作的次数。

解答:
这里就不作表了,其实把两个算法的次数表达式写出来,大家使用python或matlab等绘制函数图形的软件自然就能看出来次数差别。
A(x)=n-1
B(x)=log(2) [这里log以2为底]

1.7 全局总和例子中的第一部分(每个核对分配给它的计算值求和),通常认为是数据并行的例子;而第一个求全局总和例子的第二个部分(各个核将他们计算出的部分和发送给master核,master核将这些部分再累加求和),认为是任务并行。第二个全局和例子中的第二部分(各个核使用树形结构累加他们的部分和),是数据并行的例子还是任务并行的例子?为什么?

解答:
这里个人认为是任务并行和数据并行结合的例子。与第一个的差别是这里去计算累加和的核要多很多,不过,做的工作都是一样的,所以有任务并行的特点。但其也是任务并行的,因为这里也给每个核分配了一些数据,用于对整体的一部分进行求和。

1.8 假如系里老师要为学生举办一个聚会。
a. 在准备聚会的时候,如何分配各种任务给各个老师,以实现任务并行?设计一个方案使得各个任务能够同时进行。
b. 我们希望其中的一项任务是清理聚会场地,那么该如何分配清扫任务以实现数据并行?
c. 设计一个任务并行和数据并行结合的方案来准备聚会(如果教师的工作量太大,可以让助教来帮忙)

解答:
【引用】
任务并行是指将待解决问题所需要执行的各个任务分配到各个核上执行。
数据并行是指将带解决问题所需要的数据分配给各个核,每个核在分配到数据集上执行大致相似的操作。
a. 将不同的任务分配给各个老师就好。比如,美术老师来画海报,语文老师来写文稿,体育老师进行场地布置等等。
b. 给不同的老师或助教划定一片区域即可,注意一下大家的区域不要重合就好。
c. 比如,聚会需要搭建音乐舞台,有些老师或助教需要去布置灯光,有些老师或助教需要去调音响,有些老师或助教需要去完成音乐舞台的装饰。

1.9 写一篇文章来描述你研究方向中因使用并行计算而获益的事。大致地描述如何使用并行的。你将用到任务并行还是数据并行?

解答:
文章就不写了吧。这里的确是有这样的事。当然,两种并行方式都有用到,不过用的比较多的可能是任务并行。

  • 26
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 《并行程序设计导论》是一本讲解并行程序设计导论教材。这本教材采用了中文扫描版的形式,为读者提供了学习并行程序设计的入门材料。 并行程序设计是指将任务划分为多个子任务,各个子任务同时执行,以提高程序运行效率的一种编程方式。本教材的主要目的是向读者介绍并行程序设计的基本概念、技术和方法,帮助读者理解并掌握并行程序设计的基本原理和技巧。 《并行程序设计导论》中首先介绍了并行计算的基本概念和背景知识,包括并行计算的定义、分类和发展历程等。接着,教材详细介绍了并行程序设计的基本原理和模型,包括并行程序执行流程、并行计算机体系结构、并行程序设计的模型和并行算法等内容。 此外,教材还涵盖了常见的并行计算问题和解决方法,如任务划分、任务调度和同步等。通过具体的案例分析和实例演示,教材向读者展示了如何应用并行程序设计技术来解决实际问题。 《并行程序设计导论》还提供了丰富的习题和实践项目,帮助读者巩固所学知识,并提高并行程序设计能力。通过学习本教材,读者可以全面了解并行程序设计的基本概念和技术,为进一步深入学习并行计算领域打下坚实的基础。 总之,《并行程序设计导论》是一本介绍并行程序设计基础知识和技术的教材,通过中文扫描版的形式向读者提供了便捷的学习材料。无论是对于即将进入并行计算领域的学生还是从事并行程序设计工作的技术人员,本教材都是一本理解并应用并行程序设计的好资源。 ### 回答2: 《并行程序设计导论》是一本介绍并行程序设计的教材,其中文扫描版.pdf是该教材的电子版,方便读者在线阅读和学习。该教材主要从并行程序设计的概念、原理和技术等方面进行讲解,旨在帮助读者理解和掌握并行程序设计的基本知识和方法。 在教材中,作者首先介绍了并行程序设计的背景和意义,指出了并行计算在高性能计算、大数据处理和人工智能等领域的重要作用。然后,作者详细讲解了并行计算的基本概念,包括并行性、并行计算模型和并行编程模型等。同时,作者还介绍了常用的并行计算技术,如共享内存并行和分布式并行等。 教材还特别强调了并行程序设计的挑战和难点。由于并行计算涉及到多个计算单元的协同工作,因此需要考虑任务分配、数据共享和同步等问题。为了解决这些问题,教材提供了一系列的并行编程技术和工具,如线程、进程、消息传递和并行算法等。 在教材的最后,作者总结了并行程序设计的关键要点,并给出了一些实际应用案例,以帮助读者更好地理解和应用所学知识。此外,教材还附带了一些习题和实验,供读者进行实践和巩固所学内容。 总之,《并行程序设计导论》是一本系统介绍并行程序设计的教材,适合计算机科学、信息工程和相关专业的学生和从业人员阅读和学习。希望读者通过学习这本教材,能够深入理解并行程序设计的原理和方法,从而提升自己在并行计算领域的能力和水平。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值