IPCC Preliminary SLIC Case1/2/3

case 1

default

enforce_intel

case 2

default

enforce_intel

case 3

default

enforce_intel

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

IPCC Preliminary SLIC Optimization 6: Non-blocking MPI

非阻塞MPI

MPI_Send & MPI_receive

MPI_AllTogether()更慢,需要4s

手动向量化对齐

debug

1
2
vx = _mm256_set_pd(x); #改成
vx = _mm256_set_pd(x+3,x+2,x+1,x);

发现不对劲,打印更多输出。第一次循环肯定是对的因为和DBL_MAX比较。

需要进一步的研究学习

为什么明明有56GB的IB网,传输速度还是这么慢呢?写比较慢?

7*8=56 8条通道

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

Hybrid Multithreaded/OpenMP + MPI parallel Programs

混合编程需要注意的问题

https://www.nhr.kit.edu/userdocs/horeka/batch_slurm_mpi_multithread/ 看这个

还有个ppt 16

google hydrid openmpi openmp

intelmpi 编译

这里值得要注意的是,似乎直接用mpif90/mpicxx编译的库会报错,所以需要用

icc -openmp hello.cpp -o hello -DMPICH_IGNORE_CXX_SEEK -L/Path/to/mpi/lib/ -lmpi_mt -lmpiic -I/path/to/mpi/include
其中-DMPICH_IGNORE_CXX_SEEK为防止MPI2协议中一个重复定义问题所使用的选项,为了保证线程安全,必须使用mpi_mt库

对于intel的mpirun,必须在mpirun后加上-env I_MPI_PIN_DOMAIN omp使得每个mpi进程会启动openmp线程。

通过export OMP_NUM_THREADS来控制每个MPI产生多少线程。

OpenMPI 如何实现mult-thread(OpenMP)2

检查编译安装支持mult-thread

1
2
3
shell$ ompi_info | grep "Thread support"
Thread support: posix (MPI_THREAD_MULTIPLE: yes, OPAL support: yes, OMPI progress: no, Event lib: yes)
shell$

“MPI_THREAD_MULTIPLE: yes”说明是支持的。

在C程序里支持mult-thread

1
2
3
4
5
6
7
8
9
10
11
12
#include <mpi.h>
int MPI_Init_thread(int *argc, char ***argv,
int required, int *provided)

argc
C/C++ only: Pointer to the number of arguments.
argv
C/C++ only: Argument vector.
required
Desired level of thread support (integer).
provided
Available level of thread support (integer).

required 可选值 分别是0,1,2,3

1
2
3
4
5
6
7
8
MPI_THREAD_SINGLE
Only one thread will execute.
MPI_THREAD_FUNNELED
If the process is multithreaded, only the thread that called MPI_Init_thread will make MPI calls.
MPI_THREAD_SERIALIZED
If the process is multithreaded, only one thread will make MPI library calls at one time.
MPI_THREAD_MULTIPLE
If the process is multithreaded, multiple threads may call MPI at once with no restrictions.

MPI_Init_thread调用MPI_thread_SINGLE等同于调用MPI_Init。

注意

3.1.6的多线程支持还在初级阶段。开销很高(虽然我不知道为什么)

需要进一步的研究学习

学习MapReduce或者Hadoop? pthread vs openmp?

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

https://blog.csdn.net/Morizen/article/details/113863591

[2] OpenMPI-multThread

IPCC Preliminary SLIC Optimization 5: MPI + OpenMP

AMD

技术路线 描述 总时间 加速比 备注
Baseline 串行程序 161.7s s 1
more3omp 前面都是可以证明的有效优化 omp_num=32 14.08s
more3omp 前面都是可以证明的有效优化 omp_num=64 11.4s
deletevector 把sz大小的3个vector,移到全局变量,但是需要提前知道sz大小/声明一个特别大的 10.64s 可以看出写成全局变量也不会影响访问时间
enforce_Lscan IPCC opt 4 8.49s 19
enforce_Lscan_MPI_intel intel icpc 3.8s 42.36
Baseline2-max ppm 1.2GB ppm 10*1024*40*1024 928s
enforce_Lscan Baseline2 43.79s 21.2
enforce_Lscan_MPI_intel intel icpc + 双节点两个时间 + MPI(DoRGBtoLABConversion) 18.8s / 20s 46.4
enforce_Lscan_intel intel icpc + 单节点 15.8s 58.74 MPI(DoRGBtoLABConversion)负优化了2s
manualSIMD 13.9s
stream 13.6s
vec2mallocOMP 11.0s
mmap 10.6s
+ -O3 enforce_Lscan_intel 16.2s
+ -xHost 结果不对 17.8s
-Ofast 16.9s
-ipo 15.9s
-O3 -ipo 16.8s
-O3 -march=core-avx2 -fma -ftz -fomit-frame-pointer 16.0s
g++ suggested options -O3 -march-znver1 -mtune=znver1 -fma -mavx2 -m3dnow -fomit-frame-pointer 18.1s
g++ suggested options2 -O3 -march-znver2 -mtune=znver2 -fma -mavx2 -m3dnow -fomit-frame-pointer 19.79s
g++ -Ofast 16.9s
aocc -Ofast 16.3s
aocc suggested options 16.2s

MPI编程

由于是打算两节点两进程MPI,虽然没有OpenMP的共享内存,但是也希望通信能少一点。

PerformSuperpixelSegmentation_VariableSandM

下面关于同步区域的想法是错误的:
因为中心点移动会十分不确定,所以全部同步是最好的。

  1. 第一部分core的思路
    1. 上面numk个中心点直接一分为2,需要同步的是中间相连的$$width*(3S)$$个中心点(由于PerturbSeeds扰动,而且offset比较大,应该是中间相邻的2排,大约3S的高度的区域,上下1.5S高度)。
    2. distlab需要后面覆盖前面的(当然是计算了的区域)。klabels是取distvec更小对应的那个,应该要写个自定义归约。
    3. numk个中心点有奇数行和偶数行,经过思考后是一样的。
  2. 第二部分各中心maxlab的思路(从sz里提取numk个中心的数据)
    1. sz直接一分为2,最小同步的话,就是中间相邻中心点maxlab要max归约。
  3. 第三部分计算sz里的numk个中心点的质心和
    1. 同理,sz直接一分为2,vector相加归约同步

DoRGBtoLABConversion 0.61s

用MPI_Send写,但是一开始没注意是阻塞的,但是为什么这么慢呢?

对比之前的enforce_Lscan 8.49s

  1. DoRGBtoLABConversion 0.56s
  2. PerformSuperpixelSegmentation_VariableSandM 5.52s
    1. core 0.53s
    2. maxlab 0.02s
    3. sigma 0.03s
  3. DetectLabEdges 0.31s
  4. EnforceLabelConnectivity 1.19s
  5. PerformSuperpixelSegmentation_VariableSandM 0.88s

慢了10~20倍猜测:

  1. printf的原因? no 不打印也一样
  2. omp_num的值不对? maybe no
  3. 不在两个节点上? no
  4. g++ mpicxx? no
  5. 没有用IB ? 貌似也不是
  6. openmpi不支持openmp ? 探究方向

好像是openmp没正常运行omp_num的值为 1,32,64时间都一样。感觉是混合编程的编译问题, 而且好像是假Openmp并行,哪里有锁的样子。突然想起来,Quest的混合变成cmake需要打开multthread类似的支持,但是这里并没用。

好像也不是mpi_init_thread的问题

尝试intelmpi

果然有奇效。(结果是对的,后面我没截图了)。看到这里,可能你会觉得这个问题是OpenMPI有地方不支持openmp。但是后面有神奇的事情,如果NODELIST是fa,而不是fb就不能跑,会直接卡住。😰

首先没找到官方手册说明不同,然后研究一下这两个分区的不同。好吧从IB,cpu,内存都没区别。

限制nodelist再跑一遍。

加上打印时间,用fb分区

这个问题又没有了,但是fa分区由于经常跑可能会热一些。

最大的ppm例子

由于时间已经进5s了。所以我们需要更大的例子,再讨论2节点的开销收益,之前的例子是256034000。
这里生成了10240
40960的ppm.再大ppm程序的数组都申请不到栈空间了,需要重新数据结构。

重跑当前最快的enforce_Lscan

icpc + enforce_Lscan_MPI(DoRGBtoLABConversion)

icpc + enforce_Lscan

g++ suggested options

icpc + manualSIMD + lessLscan

icpc + manualSIMD + LscanSimple

icpc + manualSIMD + LscanSimple + stream

icpc + manualSIMD + LscanSimple + stream + mallocOMPinit

icpc + manualSIMD + LscanSimple + stream + mallocOMPinit + mmap

icpc + manualSIMD + LscanSimple + stream + mallocOMPinit + mmap + unrollLoop

放弃的原因

https://www.bilibili.com/video/BV1a44y1q782 58mins-58min50s

需要进一步的研究学习

暂无

遇到的问题

  1. 混合编程写的有问题,双节点不快反慢。怎么写呢?
  2. 那段串行代码真的不能并行吗?
  3. 向量化为什么没有提升呢,是要循环展开吗?

姜师兄建议

  1. MPI非阻塞通信 gather reduce
  2. 手动向量化

开题缘由、总结、反思、吐槽~~

参考文献

IPCC Preliminary SLIC Optimization 4: EnforceLabelConnectivity

node5/6

技术路线 描述 总时间 加速比 备注
Baseline 串行程序 207 s 1
more3omp 0.4+5+0.3 23.0s
时间细划,初始化omp 0.03+5+0.1 21.2s
不换算法,必须加锁 特别满
扫描行算法 0.03+2.2+0.1 18.5s
扫描行算法 + task动态线程池 26s
扫描行算法 + task动态线程池 + 延迟发射 26s
扫描行算法 + task动态线程池 + 延迟发射 26s
扫描行算法 + 化解重复,提高粒度:每个线程一行,不同线程杜绝同一行扫描行算法 但是没并行起来 106s
扫描行算法 + 常驻64线程 86s

初始时间

1
Time taken is 21.364595 6.066717 EnforceLabelConnectivityComputing

时间细划,初始化omp

细致划分,malloc size大小的空间不耗时,是初始化为-1耗时

1
2
3
4
Time taken is 16.963094 0.000025 EnforceLabelConnectivity	numlable
Time taken is 17.395982 0.432887 EnforceLabelConnectivity xvec yvec
Time taken is 22.668060 5.272079 EnforceLabelConnectivity iteration
Time taken is 23.001499 0.333438 EnforceLabelConnectivity klabelsComputing

修改后

1
2
3
4
Time taken is 16.063057 0.000026 EnforceLabelConnectivity       numlable
Time taken is 16.095485 0.032428 EnforceLabelConnectivity xvec yvec
Time taken is 21.116599 5.021114 EnforceLabelConnectivity iteration
Time taken is 21.237874 0.121275 EnforceLabelConnectivity klabelsComputing

改 dx4,dy4 实现访存连续性

但是可能会导致adjlabel的值不对,导致结果不对

flood fill

openmp线程池+不加锁

4分钟+, 满核结束不了,已经混乱了。

openmp线程池+加锁(单/多个)

5分钟+,满核结束不了,大翻车。

可能的原因:

  1. 本来不是计算密集型,加锁导致是串行,而且还有sz次锁的获取与释放的开销。
  2. 某个线程改了nlabels,其余运行时读取可能还要同步修改。

我又想到是不是只有一个锁,有没有多个锁的实现。还是超时结束不了。

1
2
3
4
5
6
7
8
9
omp_set_lock(&lock[nindex]); //获得互斥器
if( 0 > nlabels[nindex] && labels[oindex] == labels[nindex] )
{
xvec[count] = x;
yvec[count] = y;
nlabels[nindex] = label;
count++;
}
omp_unset_lock(&lock[nindex]); //释放互斥器

多个锁满足了nlabels的竞争,但是count的竞争还是只能一个锁。除非将数组保存变成队列才有可能,因为没计数器了。

openmp线程池+队列+(不)加锁

好耶,segmentation fault (core dumped)。果然读到外面去了。

不好耶了,并行的地方加了锁,还是会

1
double free or corruption (out) //内存越界之类的

debug 不加锁


200~400行不等seg fault。

debug 加锁

然后我打了时间戳

可以看出至少前面是正常的。

多运行几次,有时候segfault,有时corruption,我服了。

但是位置好像还是在上面的循环

每次报错位置还不一样,但是迭代的点还是对的。

队列的原子性操作需要自己加锁定义

https://stackoverflow.com/questions/32227321/atomic-operation-on-queuet

openmpfor+双队列+(不)加锁

1
munmap_chunk(): invalid pointer

黑人问号?纳尼

没办法,只能加锁,读取,写入都加锁,但是就是特别慢,4分钟+。

1
2
3
4
5
6
7
8
9
10
11
12
13
omp_set_lock(&lock); //获得互斥器
qindex = workq.front();
workq.pop();
omp_unset_lock(&lock); //释放互斥器

omp_set_lock(&lock2); //获得互斥器
if( 0 > nlabels[nindex] && labels[oindex] == labels[nindex] )
{
nlabels[nindex] = label;
workq2.push(nindex);
saveq.push(nindex);
}
omp_unset_lock(&lock2); //释放互斥器

读取,写入不是同一个队列,尝试用2个锁,还是特别慢,5分钟根本跑不完。

队列换成栈是一样的

q.front()变成了q.top()

扫描行实现

扫描线算法至少比每像素算法快一个数量级。

1
2
3
4
5
6
Time taken is 16.144375 13.062605 PerformSuperpixelSegmentation_VariableSandM 循环
Time taken is 16.144399 0.000025 EnforceLabelConnectivity numlable
Time taken is 16.177300 0.032901 EnforceLabelConnectivity xvec yvec
Time taken is 48.978709 32.801409 EnforceLabelConnectivity iteration
Time taken is 49.086252 0.107543 EnforceLabelConnectivity klabelsComputing time=49086 ms
There are 86475718 points' labels are different from original file.

不知道哪里错了,需要debug。简单debug,发现小问题。

1
2
3
4
5
Time taken is 15.670141 0.000024 EnforceLabelConnectivity      numlable
Time taken is 15.718014 0.047873 EnforceLabelConnectivity xvec yvec
Time taken is 22.103680 6.385666 EnforceLabelConnectivity iteration
Time taken is 22.219160 0.115480 EnforceLabelConnectivity klabelsComputing time=22219 ms
There are 0 points' labels are different from original file.

但是尴尬的是并没有快。哭哭哭~~~~。

优化一下变量,快了3秒,大胜利!!!

1
2
3
4
5
Time taken is 16.203514 0.000029 EnforceLabelConnectivity      numlable
Time taken is 16.234977 0.031463 EnforceLabelConnectivity xvec yvec
Time taken is 18.428990 2.194013 EnforceLabelConnectivity iteration
Time taken is 18.527664 0.098674 EnforceLabelConnectivity klabelsComputing time=18527 ms
There are 0 points' labels are different from original file.

扫描行并行实现 + 上下建线程,左右在线程里跑

用task写

虽然我在总结里写了,很难控制。但是,哎,我就是不信邪,就是玩😉

喜提segfault,打印task调用,发现task从上到下,之字形调用,而且没用一个结束的。按照设想,横向x增加比调用task快的,现在好像task堵塞的样子。

好像是没加,但是结果不对

1
2
3
#pragma omp parallel num_threads(64)
{
#pragma omp single

让我们仔细分析一下是怎么偏离预期的:

  1. (0,2)调用(0,3),(0,3)调用(0,4)很正常。但是(0,3)竟然调用了(2,4),这说明(0,3)循环到(1,3)时,发现(1,4)是已经处理的,而(2,4)是未处理的。进一步说明了(0,4)在被(0,3)创建了之后,先一步循环到(1,4),并将其处理。

  2. (0,4)先循环到(1,4),反手还调用(1,3)。然后由于(0,3)调用了(2,4)。导致(0,4)循环到后面以为到(2,4)就截止了。
  3. 虽然我说不出有什么问题,但是这不受控制的混乱调用,肯定不是我们想见的。

尝试把占用时间的print去掉。时间不短(重复调用),还是错的。(后面才发现,错误是threadcount,threadq里,每次循环完忘记清空了。日~~~)

1
2
3
4
5
6
7
8
9
10
11
Time taken is 16.226124 0.000024 EnforceLabelConnectivity      numlable
Time taken is 16.258697 0.032573 EnforceLabelConnectivity xvec yvec
Time taken is 26.320222 10.061525 EnforceLabelConnectivity iteration
Time taken is 26.401399 0.081177 EnforceLabelConnectivity klabelsComputing time=26401 ms
There are 86588716 points' labels are different from original file.

Time taken is 15.743455 0.000025 EnforceLabelConnectivity numlable
Time taken is 15.773654 0.030198 EnforceLabelConnectivity xvec yvec
Time taken is 26.348979 10.575326 EnforceLabelConnectivity iteration
Time taken is 26.442129 0.093150 EnforceLabelConnectivity klabelsComputing time=26442 ms
There are 0 points' labels are different from original file.

现在的想法是要有先后顺序,把对(x,y)一行都处理完,再发射task。或者采取延迟发射的。

延迟发射

  1. 把发射任务(x+delay,y)用队列存储,每次循环check一下,最后循环结束后,在全部发射。
  2. 或者标记(x+delay,y)发射(x,y)。但是对于循环结束后的,不好处理。
1
2
3
4
5
Time taken is 17.344073 0.000027 EnforceLabelConnectivity      numlable
Time taken is 17.377535 0.033462 EnforceLabelConnectivity xvec yvec
Time taken is 28.461901 11.084366 EnforceLabelConnectivity iteration
Time taken is 28.544698 0.082797 EnforceLabelConnectivity klabelsComputing time=28544 ms
There are 86588716 points' labels are different from original file.

很奇怪,结果不对。难道是delay的值太小。

把delay的值从10调整到750,甚至是2600,大于宽度了,结果还是不对。这是不对劲的,因为这时相当于把对(x,y)一行都处理完,再发射task。

这时我才感觉到是其他地方写错了,错误是threadcount,threadq里,每次循环完忘记清空了。日~~~

delay = 2600 结果是对了,但是也太慢了,至少要比串行快啊?

1
2
3
4
Time taken is 15.538704 0.000026 EnforceLabelConnectivity      numlable
Time taken is 15.577671 0.038968 EnforceLabelConnectivity xvec yvec
Time taken is 28.233859 12.656188 EnforceLabelConnectivity iteration
Time taken is 28.332256 0.098396 EnforceLabelConnectivity klabelsComputing time=28332 ms

delay = 20 快了一点,哭

1
2
3
4
5
Time taken is 15.631368 0.000025 EnforceLabelConnectivity       numlable
Time taken is 15.661496 0.030128 EnforceLabelConnectivity xvec yvec
Time taken is 26.788105 11.126609 EnforceLabelConnectivity iteration
Time taken is 26.869487 0.081382 EnforceLabelConnectivity klabelsComputing time=26869 ms
There are 0 points' labels are different from original file.

逆向优化分析

  1. 打上时间戳

    1
    2
    3
    4
    end Time 84 32839 taken is 0.000000 dxy4
    end Time 84 32839 taken is 0.000000 threadcount
    end Time 84 32839 taken is 0.031285 core
    end Time 84 32839 taken is 0.000023 count

    说明还是并行没写好。

  2. 检查是否调用64核,htop显示是64核

  3. 猜测原因

    • 产生了大量重复的任务,还是划分的原因,上下限制了之后,左右的重复情况如何化解。
      • 每个进程一行,task分配到y%64号线程去。但是openmp的task好像不能指定线程号。
      • 任务压入第y%64个队列,线程从队列取任务。
      • eg,第3行的后面两个线程,threadcount=0,无作用。

    • 有许多任务量过小的情况,粒度不够,次数还多,导致调用产生的开销大
    • task的线程池就是不靠谱
  4. 可以行分割或列分割,根据输入

化解重复,提高粒度:每个线程一行,不同线程杜绝同一行

  • 任务压入第y%64个队列,线程从队列取任务。
    • 但是这里同一队列的写入与读取又冲突了。可以用64个双队列,一写一读。在交换的时候等待+同步。
    • 不同线程写入同一个也冲突,每个线程再来64个队列保存,同步的时候再汇总写入。

想法很美好,但是最后的效果并不是每次64线程,基本都只有1-5个任务。导致近似单线程还有调用开销。(node6有人,node5慢些)

1
2
3
4
5
6
Time taken is 36.212269 32.876626 PerformSuperpixelSegmentation_VariableSandM 循环
Time taken is 36.212297 0.000028 EnforceLabelConnectivity numlable
Time taken is 36.247536 0.035239 EnforceLabelConnectivity xvec yvec
Time taken is 106.097341 69.849805 EnforceLabelConnectivity iteration
Time taken is 106.204154 0.106813 EnforceLabelConnectivity klabelsComputing time=106204 ms
There are 0 points' labels are different from original file.


这个原因感觉是一开始只有1个,然后一般也就产生1/2个任务。将其初始任务改成64个就行。

但是如何一开始启动64个呢,我又提前不知道任务

常驻64线程

写完又是segFault,debug

  1. [64][64][10000]太大了,每次的队列应该没这么多[64][64][100]
  2. 对于结束的统计,要用同步一下,需要加critical。结果就对了
    但是,这也太慢了
    1
    2
    3
    4
    5
    Time taken is 28.219408 0.000017 EnforceLabelConnectivity       numlable
    Time taken is 28.271994 0.052586 EnforceLabelConnectivity xvec yvec
    Time taken is 83.591540 55.319546 EnforceLabelConnectivity iteration
    Time taken is 83.696990 0.105450 EnforceLabelConnectivity klabelsComputing time=83696 ms
    There are 0 points' labels are different from original file.

受控的分段任务

没时间研究

openmpfor+特殊双数组(1+4?)

没时间研究

需要进一步的研究学习

感觉要自己写个结构体

  1. 数据可以无序
  2. 最好数据各异
  3. 支持并行读每个元素(数组?
  4. 支持并行写一堆元素,并且维护size大小

遇到的问题

暂无

并行总结

在这次并行中,让我意识到几点

  1. 任务的划分一定不能重复,相互干扰。比如,四邻域泛洪任务重复会导致竞争问题,需要加锁。但是,描绘线,任务不重复,直接避免了加锁的低效。而且重复会导致计算重复,同时占用线程。
  2. 并行任务的结果,如果不是一定要存在同一个变量就分开存,既不需要线程私有变量,最后归约;也不会存同一个位置导致竞争。比如,这次的任务会产生一堆不相关的index,那直接每个线程一个数组存,既不会冲突,之后还能接着并行。或者用更大的sz大小数组存index,结果更不会冲突了。
  3. 对于任务数增加且不确定的情况,不推荐使用task进程。因为自动调度很难控制,既不知道迭代了多少,也不确定之后会不会有隐藏的竞争。 推荐类似双队列的调度,确定一批任务,并行一批任务,同步一批任务的结果,然后重新并行。
    1. 问题:中间并行一批任务的时候还是记得分开存结果。同步的时候再处理一下就行。
    2. 双队列可能有任务量过少的问题,导致变单线程。
    3. 想到了一种启动64常驻线程,产生任务又等待任务的结构。但是问题是:任务的保存要满足产生任务的写入和处理任务的读取。在不考虑写爆的情况(循环),维护数组的写入与读取位置是可行的。任务的结束通过每个线程在读取不到任务时,判断自己发布的所有任务也被完成了,标记自己发布的任务完成了。所有发布的任务都完成,再结束。

好吧,我感觉我分析了一堆,就是在放屁。还是串行快,这个问题就难划分。就不是并行的算法。

编程总结

这次编程遇到的问题,大多数如下:

  1. 对每次循环开始时所以变量的清空,重新赋初值
  2. 结束时,全部清空。

参考文献

IPCC Preliminary SLIC Optimization 3

node6

因为例子太小,导致之前的分析时间波动太大。所以写了个了大一点的例子,而且给每个函数加上了时间的输出,好分析是否有加速。(Qrz,node5有人在用。

技术路线 描述 总时间 加速比 备注
Baseline 串行程序 207 s 1
simpleomp 两处omp 57s
more1omp maxlab 48s
more2omp sigma + delete maxxy 24.8s 8.35
more3omp DetectLabEdges + EnforceLabelConnectivity(该算法无法并行) 21.2s
icpc 13.4s
+ -O3 13.2s
+ -xHost 13.09s
+ -Ofast -xHost 基于icpc 12.97s
+ -ipo 12.73s 16.26
-no-prec-div -static -fp-model fast=2 14.2s 时间还多了,具体其他选项需要到AMD机器上试

Baseline 207s

  1. DoRGBtoLABConversion 10.4s
  2. PerformSuperpixelSegmentation_VariableSandM 187.3s
    1. core 15.3s
    2. maxlab 1s
    3. sigma 2.3s

simpleomp 57s

  1. DoRGBtoLABConversion 0.89s
  2. PerformSuperpixelSegmentation_VariableSandM 46s
    1. core 0.94-1.8s
    2. maxlab 1s
    3. sigma 2.3-2.6s

more1omp 48s

  1. DoRGBtoLABConversion 0.82s
  2. PerformSuperpixelSegmentation_VariableSandM 37s
    1. core 1-2.3s
    2. maxlab 0.04-0.1s
    3. sigma 2.3s

more2omp 24.8s

  1. DoRGBtoLABConversion 0.85s
  2. PerformSuperpixelSegmentation_VariableSandM 13.5s
    1. core 0.8-1.7s
    2. maxlab 0.02-0.1s
    3. sigma 0.1s
  3. DetectLabEdges 3.7s
  4. EnforceLabelConnectivity 5.2s

more2omp 21.2s

  1. DoRGBtoLABConversion 0.74s
  2. PerformSuperpixelSegmentation_VariableSandM 12.3s
    1. core 1.1s
    2. maxlab 0.02-0.1s
    3. sigma 0.1s
  3. DetectLabEdges 0.7s
  4. EnforceLabelConnectivity 5.8s (需要换算法
  5. PerformSuperpixelSegmentation_VariableSandM (vector声明的时间,可以考虑拿到外面去) 1.6s

icpc 13.4s

  1. DoRGBtoLABConversion 0.44s
  2. PerformSuperpixelSegmentation_VariableSandM 8.49s
    1. core 0.5-1.1s
    2. maxlab 0.04s
    3. sigma 0.05s
  3. DetectLabEdges 0.54s
  4. EnforceLabelConnectivity 2.79s (需要换算法
  5. PerformSuperpixelSegmentation_VariableSandM (vector声明的时间,可以考虑拿到外面去) 1.16s

12.7s

  1. DoRGBtoLABConversion 0.42s
  2. PerformSuperpixelSegmentation_VariableSandM 7.98s
    1. core 0.5-1.1s
    2. maxlab 0.04s
    3. sigma 0.05s
  3. DetectLabEdges 0.49s
  4. EnforceLabelConnectivity 2.69s (需要换算法
  5. PerformSuperpixelSegmentation_VariableSandM (vector声明的时间,可以考虑拿到外面去) 1.13s

IPCC AMD

技术路线 描述 总时间 加速比 备注
Baseline 串行程序 161.7s s 1
more3omp 前面都是可以证明的有效优化 omp_num=32 14.08s
more3omp 前面都是可以证明的有效优化 omp_num=64 11.4s
deletevector 把sz大小的3个vector,移到全局变量,但是需要提前知道sz大小/声明一个特别大的 10.64s 可以看出写成全局变量也不会影响访问时间
enforce_Lscan ipcc opt 4 8.49s

Baseline 161.7s

  1. DoRGBtoLABConversion 11.5s
  2. PerformSuperpixelSegmentation_VariableSandM 143s
    1. core 11.5s
    2. maxlab 0.8s
    3. sigma 1.7s
  3. DetectLabEdges 2.74s
  4. EnforceLabelConnectivity 3.34s
  5. PerformSuperpixelSegmentation_VariableSandM 1.11s

more2omp 14.08s

  1. DoRGBtoLABConversion 0.69s
  2. PerformSuperpixelSegmentation_VariableSandM 8.08s
    1. core 0.73s
    2. maxlab 0.02s
    3. sigma 0.05s
  3. DetectLabEdges 0.37s
  4. EnforceLabelConnectivity 3.8s
  5. PerformSuperpixelSegmentation_VariableSandM 1.1s

more2omp 11.4s

  1. DoRGBtoLABConversion 0.61s
  2. PerformSuperpixelSegmentation_VariableSandM 5.86s
    1. core 0.53s
    2. maxlab 0.02s
    3. sigma 0.03s
  3. DetectLabEdges 0.33s
  4. EnforceLabelConnectivity 3.5s
  5. PerformSuperpixelSegmentation_VariableSandM 1.02s

deletevector 10.64s

  1. DoRGBtoLABConversion 0.59s
  2. PerformSuperpixelSegmentation_VariableSandM 5.75s
    1. core 0.53s
    2. maxlab 0.02s
    3. sigma 0.03s
  3. DetectLabEdges 0.41s
  4. EnforceLabelConnectivity 3.84s
  5. PerformSuperpixelSegmentation_VariableSandM 0s

enforce_Lscan 8.49s

  1. DoRGBtoLABConversion 0.56s
  2. PerformSuperpixelSegmentation_VariableSandM 5.52s
    1. core 0.53s
    2. maxlab 0.02s
    3. sigma 0.03s
  3. DetectLabEdges 0.31s
  4. EnforceLabelConnectivity 1.19s
  5. PerformSuperpixelSegmentation_VariableSandM 0.88s

需要进一步的研究学习

  1. 外面声明vector
  2. EnforceLabelConnectivity 换并行算法
    1. 数据结构要求:
      1. 保存已经染色区域的位置,之后可能要还原
        1. 可以无序,有序最好,会访存连续
        2. x,y或者index也行。还是xy好判断边界
      2. 是4分还是8分,既然有重复,记录来的方向/路径,只向某方向移动。4是符合理论的,8不和要求,2有情况不能全部遍历。
      3. 3分倒是可以,但是实现小麻烦
    2. flood fill 与 PBFS 特定结合
    3. openmp线程池+锁(sz 大小的两个数组存 x y,nlabels存新的分类结果)+计时声明与flood+把这些在sz声明放外面
    4. openmp线程池+队列(最后可以并行处理吧,要一个个pop?)+需要锁吗(这取决于队列的实现有没有靠计数器)
    5. openmpfor+双队列*4/2?+需要锁吗
    6. 扫描行实现 + 上下建线程,左右在线程里跑
      1. 多线程的访问存储连续性
    7. 队列/栈是怎么实现代码的,速度怎么样(写入读取push pop,还有size)
    8. 栈有size吗
  3. 在AMD机器加入MPI进行混合编程,运行2节点

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

IPCC Preliminary SLIC Optimization 2

chivier advise on IPCC amd_256

技术路线 描述 时间 加速比 备注
Baseline 串行程序 21872 ms 1
核心循环openmp 未指定 8079ms
核心循环openmp 单节点64核 7690ms 2.84
换intel的ipcp 基于上一步 3071 ms 7.12
-xHOST 其余不行,基于上一步 4012ms
-O3 基于上一步 3593ms

node5

Intel(R) Xeon(R) Platinum 8153 CPU @ 2.00GHz

技术路线 描述 时间 加速比 备注
Baseline 串行程序 29240 ms 1
核心循环openmp 未指定(htop看出64核) 12244 ms
去除无用计算+两个numk的for循环 080501 11953 ms 10054 ms
计算融合(去除inv) 080502 15702 ms 14923 ms 15438 ms 11987 ms
maxlab openmp 基于第三行080503 13872 ms 11716 ms
循环展开?? 14436 ms 14232 ms 15680 ms

-xCOMMON-AVX512 not supports

1
Please verify that both the operating system and the processor support Intel(R) X87, CMOV, MMX, FXSAVE, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, MOVBE, POPCNT, AVX, F16C, FMA, BMI, LZCNT, AVX2, AVX512F, ADX and AVX512CD instructions.

-xCORE-AVX2

1
Please verify that both the operating system and the processor support Intel(R) X87, CMOV, MMX, FXSAVE, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, MOVBE, POPCNT, AVX, F16C, FMA, BMI, LZCNT and AVX2 instructions

没有 FXSAVE,BMI,LZCNT 有BMI1,BMI2

使用-xAVX,或者-xHOST 来选择可用的最先进指令集

1
Please verify that both the operating system and the processor support Intel(R) X87, CMOV, MMX, FXSAVE, SSE, SSE2, SSE3, SSSE3, SSE4_1, SSE4_2, POPCNT and AVX instructions.

-fast bugs

1
2
3
4
5
ld: cannot find -lstdc++
ld: cannot find -lstdc++
/public1/soft/intel/2020u4/compilers_and_libraries_2020.4.304/linux/compiler/lib/intel64_lin/libiomp5.a(ompt-general.o): In function `ompt_pre_init':
(.text+0x2281): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/var/spool/slurm/d/job437118/slurm_script: line 23: ./SLIC_slurm_intel_o3: No such file or directory

AMD EPYC 7~~2

icpc -Ofast -march=core-avx2 -ipo -mdynamic-no-pic -unroll-aggressive -no-prec-div -fp-mode fast=2 -funroll-all-loops -falign-loops -fma -ftz -fomit-frame-pointer -std=c++11 -qopenmp SLIC_openmp.cpp -o SLIC_slurm_intel_o3

后续优化

基于核心的openmp并行

去除无用计算

1
2
delete all maxxy
if(maxxy[klabels[i]] < distxy[i]) maxxy[klabels[i]] = distxy[i];

计算融合(减少访存次数)

  1. 将inv去除(效果存疑)
  2. maxlab openmp并行(由于不是计算密集的,是不是要循环展开)

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

IPCC Preliminary SLIC Analysis part4 : cluster environment

login node

id

1
2
3
4
[sca3173@ln121%bscc-a5 ~]$ id sca3173
do_ypcall: clnt_call: RPC: Remote system error
do_ypcall: clnt_call: RPC: Remote system error
uid=5804(sca3173) gid=5804(sca3173) groups=5804(sca3173),1518

cpu

1
Intel(R) Xeon(R) Silver 4208 CPU @ 2.10GHz

slurm

1
2
3
4
5
6
ON AVAIL  TIMELIMIT  NODES  STATE NODELIST 
amd_256 up infinite 3 drain* fa[0512,0911],fb1111
amd_256 up infinite 1 down* fa0714
amd_256 up infinite 1 comp fb1106
amd_256 up infinite 16 drain fa[0109,0411,0414,0601,0608-0609,0810,1203],fb[0104,0110,0513,0908,1007,1208,1212,1216]
amd_256 up infinite 377 alloc fa[0101-0108,0110-0116,0201-0202,0204-0216,0301-0316,0401-0410,0412-0413,0415-0416,0501-0511,0513-0516,0602-0607,0610-0616,0701-0713,0715-0716,0801-0809,0811,0813-0816,0901-0910,0912-0916,1001-1016,1101-1116,1201-1202,1204-1216,1301-1316],fb[0101-0103,0105-0109,0111-0116,0201-0204,0207-0216,0301-0316,0401-0416,0501-0512,0514-0516,0601-0616,0702-0716,0803-0815,0901-0904,0906-0907,0909-0911,0913-0916,1001-1003,1010,1012-1014,1016,1101,1103-1105,1107-1110,1113-1115,1201-1207,1211,1213-1214,1302-1304,1309-1315],fc[0101-0105,0115,0205-0207,0209,0215]

memery

1
2
3
4
[sca3173@ln121%bscc-a5 ~]$ cat /proc/meminfo
MemTotal: 196339948 kB = 187gB
MemFree: 89580888 kB = 85 gB
MemAvailable: 163166580 kB = 102 gB

architecture

1
2
3
4
5
[sca3173@ln121%bscc-a5 public1]$ lsb_release -d | awk -F"\t" '{print $2}'
CentOS Linux release 7.9.2009 (Core)

[sca3173@ln121%bscc-a5 public1]$ cat /proc/version
Linux version 3.10.0-1160.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) ) #1 SMP Mon Oct 19 16:18:59 UTC 2020

GPU 集显

1
2
3
4
5
6
[sca3173@ln121%bscc-a5 public1]$  lshw -numeric -C display
WARNING: you should run this program as super-user.
*-display
description: VGA compatible controller
product: Integrated Matrox G200eW3 Graphics Controller [102B:536]
vendor: Matrox Electronics Systems Ltd. [102B]

disk

1
2
3
[sca3173@ln121%bscc-a5 public1]$ df -h /public1
Filesystem Size Used Avail Use% Mounted on
10.10.58.1@o2ib:10.10.58.2@o2ib:/public1 2.7P 240T 2.3P 10% /public1

IP 内网IP

1
2
[sca3173@ln121%bscc-a5 public1]$ hostname -I | awk '{print $1}'
172.16.58.14

compute node

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
> cat lscpu.txt                
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 64
On-line CPU(s) list: 0-63
Thread(s) per core: 1
Core(s) per socket: 32
Socket(s): 2
NUMA node(s): 2
Vendor ID: AuthenticAMD
CPU family: 23
Model: 49
Model name: AMD EPYC 7452 32-Core Processor
Stepping: 0
CPU MHz: 2345.724
BogoMIPS: 4691.44
Virtualization: AMD-V
L1d cache: 32K
L1i cache: 32K
L2 cache: 512K
L3 cache: 16384K
NUMA node0 CPU(s): 0-31
NUMA node1 CPU(s): 32-63
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc art rep_good nopl nonstop_tsc extd_apicid aperfmperf eagerfpu pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_l2 cpb cat_l3 cdp_l3 hw_pstate sme retpoline_amd ssbd ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif umip overflow_recov succor smca

没有gcc/7.3.0

比赛是2节点128核的环境

计算节点网络拓扑图

我们是A5 分区。

没有找到手册,只有一个官网图。但是虽然频率是2.35GHz,但是内存只有251GB啊,什么情况。

IPCC Preliminary SLIC Analysis part3 : Hot spot analysis

vtune hotspots




vtune threading



GUN profile gprof + gprof2dot graphviz

1
2
3
4
5
6
g++ -pg -g -std=c++11 SLIC.cpp -o SLIC
./SLIC # generate gmon.out
less gmon.out
"gmon.out" may be a binary file. See it anyway?
gprof ./SLIC
gprof ./SLIC| /home/shaojiemike/github/isc21-gpaw/LogOrResult/profile/gprof2dot.py -n0 -e0 | dot -Tpng -o output.png



没什么用

接下来

  1. 向量化
  2. 并行化

什么时候OpenMP并行,什么时候MPI并行

根据具体资源情况来,貌似是一个节点,那可以从OpenMP入手

自动并行化

Intel编译器的自动并行化功能可以自动的将串行程序的一部分转换为线程化代码。进行自动向量化主要包括的步骤有,找到有良好的工作共享(worksharing)的候选循环;对循环进行数据流(dataflow)分析,确认并行执行可以得到正确结果;使用OpenMP指令生成线程化代码。

/Qparallel:允许编译器进行自动并行化

/Qpar-reportn:n为0、1、2、3,输出自动并行化的报告

说明:/Qparallel必须在使用O2/3选项下有效

c++向量化怎么实现

什么是向量化

所谓的向量化,简单理解,就是使用高级的向量化SIMD指令(如SSE、SSE2等)优化程序,属于数据并行的范畴。

如何对代码向量化

向量化的目标是生成SIMD指令,那么很显然,要对代码进行向量化,

第一是依靠编译器来生成这些指令;

第二是使用汇编或Intrinsics函数。

自动向量分析器

Intel编译器中,利用其自动向量分析器(auto-vectorizer)对代码进行分析并生成SIMD指令。另外,也会提供一些pragmas等方式使得用户能更好的处理代码来帮助编译器进行向量化。

  1. 基本向量化
    /Qvec:开启自动向量化功能,需要在O2以上使用。在O2以上,这是默认的向量化选项,默认开启的。此选项生成的代码能用于Intel处理器和非Intel处理器。向量化还可能受其他选项影响。由于此选项是默认开启的,所以不需要在命令行增加此选项。

  2. 针对指令集(处理器)的向量化
    /QxHost:针对当前使用的主机处理器选择最优的指令集优化。

对于双重循环,外层循环被自动并行化了,而内层循环并没有被自动并行化,内层循环被会自动向量化。

影响向量化的因素

  1. 首先当然是指令集是否支持
  2. 内存对齐相关的问题,也是影响向量化的,很多的SSE指令都要求内存是16字节对齐,如果不对齐,向量化会得到错误结果。

如何判断向量化成功

看汇编代码
没成功需要手动内联向量化汇编代码???

Intel 编译器的向量化实现

AMD 编译器向量化实现

AMD 与 Intel 编译器的区别

需要进一步的研究学习

暂无

遇到的问题

暂无

参考文献

https://blog.csdn.net/gengshenghong/article/details/7027186

https://blog.csdn.net/gengshenghong/article/details/7034748

https://blog.csdn.net/gengshenghong/article/details/7022459

IPCC Preliminary SLIC Analysis part2 : Run process

VScode Debug Run

Debug Process

  1. 创建lables来存储分类结果, m_spcount 200, m_compactness 10
  2. 计时运行 slic.PerformSLICO_ForGivenK(img, width, height, labels, numlabels, m_spcount, m_compactness);
    1. 参数的最后两项是required number of superpixels和weight given to spatial distance(空间距离的权重)也就是K=200
    2. 串行初始化赋值klabels[s] = -1
    3. DoRGBtoLABConversion(ubuff, m_lvec, m_avec, m_bvec);对于整个图像:重载的浮点版本
      1. 提取第一个像素rgb(229,226,218)
      2. 串行RGB2LAB( r, g, b, lvec[j], avec[j], bvec[j] );
      3. int变成double数组
    4. 声明perturbseeds true,edgemag
    5. DetectLabEdges(m_lvec, m_avec, m_bvec, m_width, m_height, edgemag);
      1. 串行对每个像素进行$$dx=(l[i-1]-l[i+1])^2+(a[i-1]-a[i+1])^2+(b[i-1]-b[i+1])^2$$$$dy=(l[i-width]-l[i+width])^2+(a[i-width]-a[i+width])^2+(b[i-width]-b[i+width])^2$$$$edgemag[i]=dx+dy$$为了之后计算6.5-PerturbSeeds
      2. 值得注意的是i的取值说2600 = width+1是第二行第二列,一直到倒数第二行倒数第二列
    6. GetLABXYSeeds_ForGivenK(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, K, perturbseeds, edgemag);
      1. int step = sqrt(double(sz)/double(K)); \(S=\sqrt{N/k}=225\)
      2. xoff 与 yoff 为一半 112
      3. int X = x*step + (xoff<<(r&0x1));六角网格???
      4. 串行对每个绿圈存了颜色坐标五元组在kseedsl/a/b/x/y 有196个中心
      5. PerturbSeeds(kseedsl, kseedsa, kseedsb, kseedsx, kseedsy, edgemag);
        1. 串行对这196个中心和周围8个位置进行,最小edgemag值的寻找。
        2. 并更新196个中心的位置
    7. int STEP = sqrt(double(sz)/double(K)) + 2.0;
    8. PerformSuperpixelSegmentation_VariableSandM(kseedsl,kseedsa,kseedsb,kseedsx,kseedsy,klabels,STEP,10);
      1. if(STEP < 10) offset = STEP*1.5;
      2. DBL_MAX ? doubel_max 1.7976931348623157e+308
      3. 变量sigma , distxy, maxxy
      4. double invxywt = 1.0/(STEP*STEP);
      5. 迭代10次
        1. 对中心聚类
          1. 串行对这196个中心首先划出[2S*2S]的区域(上下左右offset或者STEP)
          2. 再串行对这\(4S^2\)的区域的每个像素,计算与其区域中心的距离
            $$dist = \frac{(l-kl[n])^2+(a-ka[n])^2+(b-kb[n])^2}{maxlab[n]}+\frac{(x-kx[n])^2+(y-ky[n])^2}{S^2}$$
          3. 注意maxlab[n]初始值是10*10,根据dist更新该像素的距离中心的最小距离数组distvec,和指向的最近中心klable由于是2S*2S,相邻中心的周围区域是有一部分重叠的(如图中黄色荧光笔区域),相当于聚类到各个中心,注意由于中心对自己dist=0,是不可能某一中心距离其他中心更近。
        2. 串行将maxlab与maxlab更新为该聚类集合里的最大值
          1. 注意maxlab与maxlab都是每个中心维护一个
        3. 质心移动:串行将每个聚类区域的每个点的五元组加到质心上,然后除以聚类区域元素总数来得到新的质心五元组
        4. 迭代10次结束(其实可以判断质心是否不再移动来提前结束)
    9. EnforceLabelConnectivity(klabels, m_width, m_height, nlabels, numlabels, K);
      1. 串行新数组赋值for( int i = 0; i < sz; i++ ) nlabels[i] = -1;全部标记为未处理
      2. 串行对所有元素进行如下处理
        1. 如果旧index未被处理
          1. 在其左下上右找到已经处理的元素的nlable,将其值保存在adjlable里
          2. 从该元素开始上下左右寻找未处理,而且其klabels值与旧klabels值相同的元素。
            1. 将其nlable值改为lable,应该也达到标记为已处理的效果
            2. 最终效果就是把该元素相邻连接区域klabels值与旧klabels值相同的全部标记
          3. 如果这块相邻区域过小count <= SUPSZ >> 2
            1. 则将其全部元素赋值为adjlable,即并入上块区域
        2. 旧index++。
        3. 最终效果就是所有的元素重新整合聚类,达到消除过小区域与不连续区域的效果
  3. 结果与check.ppm对比
    1. 我以为只要读取lable的分类,大概sz个int
    2. fread(rgb, (w)*(h)*3, 1, fp);
    3. 作者是傻逼,只用了1/3的空间

skills: size of arrays

1
a.size()

相关知识

RGB与Lab颜色空间互相转换

Lab颜色空间简介

同RGB颜色空间相比,Lab是一种不常用的色彩空间。1976年,经修改后被正式命名为CIELab。Lab颜色空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;a表示从红色到绿色的范围,取值范围是[127,-128];b表示从黄色到蓝色的范围,取值范围是[127,-128]。

RGB转Lab颜色空间

RGB颜色空间不能直接转换为Lab颜色空间,需要借助XYZ颜色空间,把RGB颜色空间转换到XYZ颜色空间,之后再把XYZ颜色空间转换到Lab颜色空间。

RGB与XYZ颜色空间有如下关系:

LAB与XYZ颜色空间有如下关系:


X,Y,Z会分别除以0.950456、1.0、1.088754。

需要进一步的研究学习

暂无

遇到的问题

暂无

参考文献