文章目录
本次实验结合Arm平台NEON指令集和X86平台的AVX指令集来对高斯消元算法进行pthread的多线程优化,并探究不同优化方法(信号量或barrier同步)、编程策略的优化幅度;此外,本次实验还将在x86平台上利用OpenMP进行高斯消元算法的多线程优化,并探究不同任务划分方案的影响。
关键字: pthread,OpenMP,ARM,x86
实验平台: x86平台,Windows 10 64位操作系统,CPU型号AMD
R7-5800h,8核16线程,主频3.2GHz,单核拥有32KB一级数据、32KB一级指令缓存、512KB二级缓存、共享16MB三级缓存;ARM平台利用华为鲲鹏服务器;
编译选项: 各实验不同
代码链接: 多线程优化代码
pthread 并行优化
POSIX Threads简称Pthreads,POSIX是"Portable Operating System
Interface"(可移植操作系统接口)的缩写,在C++/C程序中pthread能让进程在运行时可以分叉为多个线程执行,并在程序的某个位置同步和合并,得到最终结果。
pthread处理方案
在整个高斯消元算法中,主要有第一部分的除法循环,和第二部分的消去循环。其中第一部分循环的时间复杂度为 O ( n ) O(n) O(n),而第二部分的复杂度为 O ( n 2 ) O(n^2) O(n2),在处理数据规模大的矩阵时(我们的目的也是优化高斯消元算法面对大规模数据的效率),第一部分的工作量远远小于第二部分,且第一部分并不耗费资源,在整个算法的耗时中占比较少,因此我们着重讨论对于第二部分消去循环的pthread并行优化。
pthread的优化大概有两种范式:
-
动态线程,即每次到达可以并行优化的部分才创建线程进行并行计算,这种方法较为灵活,不会占用系统资源,在把控好线程数时也会有较好的优化效果;但是缺点是会增加频繁创建和销毁线程的时间开销;
-
静态线程,即一开始就创建好线程,运行至需要并行计算的部分再为各线程分配任务,直到整个算法完毕才销毁线程,该方法的优缺点刚好与动态线程相反。
本次实验会讨论以上两种编程范式,而在具体的实现方法(线程分配、同步方式)上,将实现并测试以下方法:
-
动态分配线程,每次消去时创建线程,一个线程负责一行的消去,这样每次会创建N-1-k个线程,通过建立一个线程函数来实现每条线程的工作,并调用 p t h r e a d _ c r e a t e ( ) pthread\_create() pthread_create()等函数控制线程的创建与销毁;
-
静态分配线程,固定分配8个线程,并通过sem_t信号量进行同步。信号量机制通过信号量值控制可用资源的数量。线程访问共享资源前,会申请一个信号量,若信号量为0,说明当前无可用的资源,线程无法获取信号量,则该线程会等待其他资源释放信号量(信号量加1)。如果信号量不为0,说明当前有可用的资源,此时线程占用一个资源,对应信号量减1。通过 s e m _ w a i t 和 s e m _ p o s t sem\_wait和sem\_post sem_wait和sem_post函数来实现信号量的申请与释放,结合pthread的线程API函数来进行线程管理;
-
静态分配线程,固定分配8线程,在sem_t信号量同步同时将三重循环纳入线程函数。相比上一个方法,该方法将除法部分循环也放入了线程函数,只不过这一部分仅由0号线程执行,剩下的线程会一同执行行消去部分的并行计算,理论上这样的方法也会带来一定的性能提升;
-
静态分配线程,固定分配8线程,并通过barrier进行同步。该方法创建两个pthread_barrier_t变量,能够控制各线程在某个地点进行同步,只有当所有的线程都达到这个barrier时,程序才继续执行,否则到达此处的线程将被阻塞,这种方法能够方便简洁的实现线程的同步。
ARM平台上的phtread优化
ARM平台上结合NEON指令集的pthread优化主要思路以及代码结构和实验指导书中的一致,具体代码可见NEON指令集pthread优化
编译选项:clang++ -g -armv8=a -lpthread pthread.cpp -o pthread
结果
在500、1000、1500、2000的数据规模下,经多次测试后得到实验数据如下:(单位:s)
500 | 1000 | 1500 |
---|