栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

奇牛C/C++零基础到年薪30w全栈工程师

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

奇牛C/C++零基础到年薪30w全栈工程师

 c++转置并行化

代码实现

unsigned char* pSource;

pSource = (unsigned char*)malloc(sizeof(unsigned char) * NREALCOL * NROW);

for (int irow = 0; irow < NROW; ++irow)

{

memset(pSource + irow * NREALCOL, irow % 256, sizeof(unsigned char) * NREALCOL);//按照字节赋值

}

unsigned char* pTarget;

InitMem(pTarget);

clock_t begin = clock();

int nbC = NCOL / BLOCK, nbR = NROW / BLOCK;

for (int i = 0; i < REPEAT; ++i)

 

{

#pragma omp parallel for num_threads(8) schedule(static)

for (int ibr = 0; ibr < nbR; ++ibr)

{

for (int ibc = 0; ibc < nbC; ++ibc)

{

for (int irow = 0; irow < BLOCK; ++irow)

{

for (int icol = 0; icol < BLOCK; ++icol)

{

pTarget[(ibr * BLOCK + irow) * NROW + icol] = pSource[(ibc * BLOCK + icol) * NREALCOL + irow];

}

}

}

}

}

clock_t end = clock();

std::cout << "PaddingTranspose 1024 Time " << (end - begin) << std::endl;

std::cout << "PaddingTranspose parallel Time (ms) " << ((float)(end - begin)) / (float)REPEAT << std::endl;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

代码中for循环使用静态的策略。

执行时间

PaddingTranspose parallel Time (ms) 0.114258

1

同时,也尝试了对for循环使用不同的调度模式。

运行时动态调度

#pragma omp parallel for num_threads(8) schedule(dynamic)

1

执行时间

PaddingTranspose parallel Time (ms) 0.105469

1

默认调度

#pragma omp parallel for num_threads(8)

1

执行时间

PaddingTranspose parallel Time (ms) 0.121094

1

三种并行化方式,对比Base版本,得到的最大加速比为 0.367188 / 0.105469 = 3.48。加速比并不理想。

测试执行耗时排序:

动态调度 < 静态调度 < 默认调度

静态调度方法,理论上每个线程的负载是一样的,原则上应该是static的方式运行速度应该最快。基于我的认知,尝试解释当前执行速度不符合预期的原因:实际程序运行情况与理论存在一定的偏差,由于矩阵尺寸太小,每个线程的负载太低,同时线程启动,同步等耗时不均匀,并行区线程启动等耗时占用比例过大,造成了目前这样的现象。

VTune分析

函数耗时统计,可以看到OpenMP线程同步占一定的比重。

并行区线程执行情况。

并行区内8个线程在有效执行指令中,夹杂着大量同步等待时间。

造成这一点的可能原因(个人理解):

并行化过程包括创建并行区,各线程执行分配来的任务,并行区析构。每个过程的执行都要消耗一定的CPU执行时间。同时,一次并行执行的过程中,每个线程的只是对128 * 1024个Byte数据进行内存搬运,线程负载太小。因此,当前的实现方式(频繁的创建和析构并行区)造成多线程利用率低,多线程加速比不理想。

此外,尝试了在for循环最外层开辟并行区,内层循环用omp for迭代,避免并行区的频繁开辟,效果不明显,执行时间几乎没变化。

试验OpemMP并行方案

针对通过OpenMP对for循环的并行处理,可引申出两个问题:

1.OpenMP的调度方式static/dynamic对执行性能的影响

2.开辟并行区位置对执行性能的影响

为了测试上述影响,只需要对两个因素分别做更改并依次试验:并行区位置与调度方式。此外,为了排除线程负载过小带来的干扰,加大线程的负载,将二维矩阵尺寸更改为 32k * 32k。一次迭代每个线程要处理数据128M。

矩阵尺寸相关定义,其中把矩阵尺寸更新成32K,同时为了避免运行时间过长,通过调成重复次数至50次。

#define NROW 1024*32

#define NCOL 1024*32

#define NREALCOL (NCOL + 128)

#define NSLICE NROW*NCOL

#define REPEAT 50

#define BLOCK 128

1

2

3

4

5

6

内部开辟并行区,静态调度

代码实现

for (int i = 0; i < REPEAT; ++i)

{

#pragma omp parallel for num_threads(8) schedule(static)

for (int ibr = 0; ibr < nbR; ++ibr)

{

for (int ibc = 0; ibc < nbC; ++ibc)

{

for (int irow = 0; irow < BLOCK; ++irow)

{

for (int icol = 0; icol < BLOCK; ++icol)

{

pTarget[(ibr * BLOCK + irow) * NROW + icol] = pSource[(ibc * BLOCK + icol) * NREALCOL + irow];

}

}

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

执行时间

PaddingTranspose parallel Time (ms) 120.84

1

VTune数据

可以看到,代码执行时,并行区析构前的同步占了一定的比重,影响了程序执行的性能。

代码内部开辟并行区,动态调度

代码实现

for (int i = 0; i < REPEAT; ++i)

{

//#pragma omp for schedule(dynamic)

#pragma omp parallel for num_threads(8) schedule(dynamic)

for (int ibr = 0; ibr < nbR; ++ibr)

{

for (int ibc = 0; ibc < nbC; ++ibc)

{

for (int irow = 0; irow < BLOCK; ++irow)

{

for (int icol = 0; icol < BLOCK; ++icol)

{

pTarget[(ibr * BLOCK + irow) * NROW + icol] = pSource[(ibc * BLOCK + icol) * NREALCOL + irow];

}

}

}

}

}

1

2

3

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934866.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号