并行设计方法

2021SC@SDUSC

并行程序优化SLIC超像素算法

配置并行程序运行环境

简单起见直接采用openmp(g++/gcc自带)

gcc main.cpp -fopenmp

源代码(未优化)

源代码是未并行的程序

SLIC.h


SLIC.cpp


运行结果如下:

Computing time=26789 ms
There are 0 points' labels are different from original file.

后面是付费内容

intel编译器配置

  1. intel官网安装toolkit,下载地址

  2. 下载完成之后,安装,一直continue下一步即可

    image-20211122171045628

    安装目录默认在C:Program File(x86)/Intel/oneAPI

  3. 执行"C:\Program Files (x86)\Intel\oneAPI\setvars.bat"初始化环境变量

    对于powershell执行 cmd.exe "/K" '"C:\Program Files (x86)\Intel\oneAPI\setvars.bat" && powershell'

  4. 在项目目录下运行 oneapi-cli.exe

计时器(精确到微秒)

#include <ctime>
timeval t1 = timeval();
timeval t2 = timeval();
mingw_gettimeofday(&t1, nullptr);
// some code
mingw_gettimeofday(&t2, nullptr);
// ms显示
printf("\nfinish: time %d ms", (t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000);

openmp计时器

#include <omp.h>

double start_time, end_time;
start_time = omp_get_wtime();
//do some functions
end_time = omp_get_wtime();
printf("Spend time: %.3lf s\n", end_time - start_time);

整数与浮点运算速度

先上结论,整数(int,long)与浮点数(float,double)在±*方面速度几乎一致

在除法中浮点数除法明显慢于整数出发

浮点数的乘法会略快于整数乘法

debug

在数千万条数据里总有几个与众不同

于是优先从聚类中心中debug(数量少)

第一次迭代的时候聚类中心获取到的像素点数量全都一致

image-20211123235243549

进一步查看聚类中心的变换情况,发现从第56个聚类中心开始数据有变动,因此聚集的像素点数量相同,但是实际像素不同,或者是像素的转化出现问题,结合之前直接导入代码没有问题,应该不是像素的转换出了问题,是实际的运算问题

image-20211124000256774

回到计算聚类的平均质心之前的求和部分,发现加法的时候已经出现了问题,而且问题出现在的57号聚类中心恰好有两个nan的存在,应该是两个nan导致后面所有的求和都出现了问题

进一步发现问题出现在了labxy中的bx两个向量上

对初始化的数组进行输出没有nan !还真有nan确定问题是初始化时忘记五维向量的系数

解决这个问题之后发现结果还是不对

再走流程,发现聚类中心在第一次迭代是一致的

通过二分法,找到第一次不一致的地方,发现从第二次开始又不对了

原来是初始化有两个地方,忘了改另一个了

初始化一定要检查清楚,感觉没问题出了问题,一定要检查初始化语句是不是写对了

优化记录

面向对象与面向过程优化

原来的程序时基于面向对象的,面向对象就必然有类的开销,优化程序的过程中先优化面向对象

  1. 将所有类的变量提取出来

  2. 将所有的方法孤立出来,通过全局变量传参

    这里使用全局变量主要是为了方便后期将所有的函数合一,减少函数调用时间

  3. 替换所有的vector为数组,在使用vector的时候,也是为了便利性牺牲了一定的性能,通过vector的替换可以省去许多函数调用,提高性能,利用数组与指针更加贴近于汇编操作,方便编译器进行优化操作

  4. 自己实现vector数组替换原始的面向对象的vector

优化赋值计算操作

  1. 原始的所有赋值操作是通过for循环进行赋值

  2. 用到系统自带的memset与memcpy会显著的提升赋值的性能

    通过测试1^9次赋值操作大约需要1s

  3. 优化浮点除法,浮点除的性能明显低于浮点±*

  4. 将浮点除法转换为浮点乘法,在编译期间就计算出可以计算的除法的结果

  5. 利用cebt立方根函数,代替pow函数

使用缓存替代计算

  1. 在颜色转换中对rgb转换成lab需要进行多次浮点运算以及多次立方运算,性能很低
  2. 首先颜色空间的转换需要先从rgb转换为xyz,这一过程需要的是一次pow(x,2.4)的运算,考虑对于固定的rgb每次的起始运算都一致,只需要提前计算256种颜色的pow运算就可以省区后续的pow,只需要查表就可以了,大约优化0.2s
  3. 有多个颜色可能会同时出现在界面上,这个时候可以利用hashmap一类的函数,对颜色转换进行缓存,也可以省去许多浮点运算以及pow运算(在多线程的条件下,缓存命中率会明显下降,效率反而降低)

优化cache命中率

  1. 对于原始的cache,由于rgb与lab都是分开存储的cache的命中率不一定很高
  2. 使用三维数组直接存放rgb与lab相邻的lab与rgb基本上一定会命中cpu cache会提高效率 大约优化0.4-0.6s
  3. 对线程进行较大块的分割减少线程消耗同时,增加部分命中率
  4. 进行字节对齐提高内存读取速度

优化多次相同操作 smid

对于相似的操作,只是单纯的利用多核效果并不出色,可以考虑到,由于cpu拥有多个运算单元,往往利用向量化,可以同时进行多个运算单元并行操作进一步提高并行化效果

原始代码如下所示

seeds[kn + 0] = sigma[kn + 0] * inv;
seeds[kn + 1] = sigma[kn + 1] * inv;
seeds[kn + 2] = sigma[kn + 2] * inv;
seeds[kn + 3] = sigma[kn + 3] * inv;
seeds[kn + 4] = sigma[kn + 4] * inv;

这五个明显是几乎相同的代码,因此可以进行向量化操作

#pragma omp simd
for (int i = 0; i < 5; ++i) {
    seeds[kn + i] = sigma[kn + i] * inv;
}

多线程并行

利用openmp可以实现多线程并行,下面一行就是for循环的迭代过程

#pragma omp parallel for default(none) schedule(guided) shared()

对于schedule,经过系统测试,设置为guid,且把最小值设置为8的性能最高

信仰优化

使用编译器O3优化 (O3不如O2快,不知是不是个例)

这部分优化时间不确定,在初始代码中大约可以从35s优化到30s

经过重写改进的代码大约可以从10s优化到4s

经过并行改进的代码大约可以从4s优化到2s

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值