基于OpenMP和PThread的多线程矩阵乘法及分析

  本文主要记录了基于OpenMP和PThread实现不同线程、不同矩阵大小的矩阵乘法实例及其分析,代码注释比较详细,以供参考。

  相关代码也可以直接访问git,这里贴上链接:

https://github.com/zly5/Parallel-Computing-Labicon-default.png?t=N7T8https://github.com/zly5/Parallel-Computing-Lab

在这之前先让我们搞明白两个个名词——加速比和效率:

  加速比:是同一个任务在单处理器系统和并行处理器系统中运行消耗的时间的比率,用来衡量并行系统或程序并行化的性能和效果。其定义如下式所示:

S_p=T_p/T_1

其中,Sp是加速比,T1是单处理器下的运行时间,Tp是在有p个处理器并行系统中的运行时间。当Spp时,此加速比被称为线性加速比。

  效率: 反映了并行系统中处理器的利用情况,其定义用下式来表示。

E_p=S_p/p

其中Ep表示效率,Sp表示加速比,p表示处理器个数。

基于OpenMP并行矩阵与矢量乘法实验

🤗先沾上完整代码如下:

#include <stdio.h>
#include <omp.h>
#include <stdlib.h>
#include <time.h>

const int NUM_THREADS = 4; //设置线程数量
int N = 10000;
int M = 10000;
int mat[10000][10000]; //矩阵mat
int vec[10000], ans[10000]; //向量vec

void makeRandomMatrix()  //生成矩阵
{
    srand(time(NULL));
    int i, j;
    for (i = 0; i < M; i++)
    {
        for (j = 0; j < N; j++)
        {
            mat[i][j] = rand() % 10 + 1;
        }
    }
}

void makeRandomVector() //生成向量
{
    srand(time(NULL));
    int i;
    for (i = 0; i < N; i++)
    {
        vec[i] = rand() % 10 + 1;
    }
}

void funy(int a[], int cur)  //计算矩阵和矢量乘的部分结果
{
    int i;
    for (i = 0; i < N; i++)
    {
        ans[cur] += a[i] * vec[i];
    }
}

void f()  //串行计算
{
    int i;
    for (i = 0; i < M; i++)
    {
        funy(mat[i], i);
    }
}

void fp() //并行计算
{
    int i;
    #pragma omp parallel for num_threads(NUM_THREADS)
        for (i = 0; i < M; i ++)
        {
            funy(mat[i], i);
        }
}

int main()
{
    printf("Makeing matrix(%d*%d) & vector(%d*1)...\n",N,M,N);
    makeRandomMatrix(); 
    makeRandomVector();
    double start_time = omp_get_wtime();
    f();
    double end_time = omp_get_wtime();
    printf("串行 --- Running time=%f s\n", end_time - start_time);
    start_time = omp_get_wtime();
    fp();
    end_time = omp_get_wtime();
    printf("%d threads --- Running time=%f s\n", NUM_THREADS,end_time - start_time);
    return 0;
}

  首先,我们考虑小矩阵(200*200)和小矢量(200)即计算量不大的情况下,多线程和串行的比较,其结果如下表1所示。

表1 200大小矩阵矢量乘法测试

串行程序

2线程

4线程

第一次

0.000277

0.000279

0.000286

第二次

0.000276

0.000270

0.000319

第三次

0.000275

0.000270

0.000268

第四次

0.000274

0.000268

0.000356

第五次

0.000276

0.000269

0.000315

平均值

0.0002756

0.0002712

0.0003088

  从以上结果可以看到,在计算规模不大时,多线程程序相比串行串行优势不大,其更大的时间花在了线程调度相关任务。因此,接下来,我们考虑大矩阵(10000*10000)和大矢量(10000)即计算量较大的情况下,多线程和串行的比较,其结果如下表2和图1所示。

表2 10000大小矩阵矢量乘法测试

串行程序

2线程

4线程

第一次

0.216982

0.124177

0.070351

第二次

0.216612

0.124312

0.072720

第三次

0.216488

0.127765

0.071641

第四次

0.246336

0.127330

0.071734

第五次

0.216362

0.123395

0.069704

平均值

0.222556

0.1253958

0.07123

图1 10000大小矩阵矢量乘法测试结果

  从表2和图1中我们可以看到在计算规模较大时,多线程才可以发挥出自己最大的能力,表2中,2线程比串行快将近一倍,4线程又比2线程快将近一倍。

  具体的加速比和效率数据如下表3所示;

表3 10000大小矩阵矢量乘法加速比和效率

串行程序

2线程

4线程

平均用时

0.222556

0.1253958

0.07123

加速比

1

1.774828184

3.124470027

效率

100%

88.7414092%

78.1117507%

  从上表3的数据及表1的数据来看,多线程的加速比和效率都和处理子问题的规模有关,若处理的规模太小,创建的线程太多,那多线程的串行将在线程调度等操作上耗费大量时间。

基于OpenMP并行矩阵乘法实验

🤗这里就只沾上完整代码了,具体的记时和测试可以和上例相同操作:

#include <iostream>
#include <pthread.h>
#include <omp.h>
#include <sys/time.h>
#include <ctime>
using namespace std;
const int maxn = 4;

int A[maxn][maxn], B[maxn][maxn], C[maxn][maxn];

int main() {
    int i, j, k;

    omp_set_num_threads(omp_get_num_procs());
    srand(time(NULL));
    for (i = 0; i < maxn; i++)
        for (j = 0; j < maxn; j++) {
            A[i][j] = rand() % 10;
            B[i][j] = rand() % 10;
        }

    #pragma omp parallel for private(i,j,k) shared(A,B,C)
    for (i = 0; i < maxn; ++i)
        for (j = 0; j < maxn; ++j)
            for (k = 0; k < maxn; ++k){
                //printf("OpenMP Test, : %d\n", omp_get_thread_num());
                C[i][j] += A[i][k] * B[k][j];
            }
                

    for (i = 0; i < maxn; i++) {
        for (j = 0; j < maxn; j++)
            cout << C[i][j] << "\t";
        cout << endl;
    }
}

PThread矩阵相乘实验

  考虑到矩阵乘法的算法过程,我们按如下思想去设计多线程程序,给定线程数NUM_THREADS和矩阵matrixA(M*N)、matrixB(N*M)。其中每一个线程计算矩阵A中的L=M/NUM_THREADS行数据和B中的所有数据相乘的结果。

🤗同样先沾上完整代码如下:

#include<stdio.h>   
#include<time.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<memory.h>
 

#define M 600
#define N 600
int matrixA[M][N];
int matrixB[N][M];
int result[M][N];

void *func(void *arg);

const int NUM_THREADS =8 ;   //线程数
pthread_t tids[NUM_THREADS];  //线程
int L;                       //每个线程计算的块大小

void makeRandomMatrix_A()  //生成矩阵
{
    srand(time(NULL));
    int i, j;
    for (i = 0; i < M; i++)
    {
        for (j = 0; j < N; j++)
        {
            matrixA[i][j] = rand() % 10 + 1;
        }
    }
}

void makeRandomMatrix_B()  //生成矩阵
{
    srand(time(NULL));
    int i, j;
    for (i = 0; i < N; i++)
    {
        for (j = 0; j < M; j++)
        {
            matrixB[i][j] = rand() % 10 + 1;
        }
    }
}

//子线程函数 
void *func(void *arg)                                  
{
    int s=*(int *)arg;      //接收传入的参数(此线程从哪一行开始计算)
    int t=s+L;              //线程算到哪一行为止
    for(int i=s;i<t;i++)                                 
        for(int j=0;j<M;j++)
            for(int k=0;k<N;k++)
                result[i][j]+=matrixA[i][k]*matrixB[k][j];                               
}

void fp(){                              //串多线程函数
    int i;
    int j = 0;    
    int t[NUM_THREADS];    //传参索引
    L = M / NUM_THREADS;  //按设置的线程数分配工作块(单个线程所要计算的行数L)

    for(i=0;i<M;i+=L)
        {
            t[j] = i;
            if (pthread_create(&tids[j], NULL, func, (void *)&(t[j]))) //产生线程,去完成矩阵相乘的部分工作量
            {
                perror("pthread_create");
                exit(1);
            }
            j++;
       } 

    for(i=0;i<NUM_THREADS;i++)
        pthread_join(tids[i],NULL);                         //等所有的子线程计算结束
}

void f(){                                                //串行程序函数
    int res[M][M]={0};                                  //保存矩阵相乘的结果。非全局变量一定要显示初始化为0,否则为随机的一个数
    for(int i=0;i<M;i++)                                 
        for(int j=0;j<M;j++)
            for(int k=0;k<N;k++)
                res[i][j]+=matrixA[i][k]*matrixB[k][j];               
}

int main()
{
    makeRandomMatrix_A();                                      //用随机数产生两个待相乘的矩阵,并分别存入两个文件中
    makeRandomMatrix_B();                                      //从两个文件中读出数据赋给matrixA和matrixB
    printf("Makeing matrix(%d*%d) & matrix(%d*%d)...\n",N,M,M,N);

    //串行计算
    clock_t start2=clock();                              //开始计时
    f();                                               //串行程序
    clock_t finish2=clock();                             //结束计算
    printf("串行 --- Running time=%f s\n", (double)(finish2 - start2) / CLOCKS_PER_SEC);

    //多线程计算
    clock_t start1=clock();                              //开始计时
    fp();                                               //多线程
    clock_t finish1=clock();                             //结束计算
    printf("%d threads --- Running time=%f s\n", NUM_THREADS,(double)(finish1 - start1) / CLOCKS_PER_SEC);    

    return 0;
}

  使用以上程序开始测试,我们测试了矩阵大小为200*200,400*400,600*600时串行程序和不同的多线程程序对应的计算时间分别为多少。具体结果如下表4、5、6所示。

表4 200*200大小矩阵乘法实验结果

线程数

时间

加速比

效率

1

0.018000

1

100%

2

0.010000

1.800000

90.0000%

4

0.008000

2.250000

56.2500%

10

0.007000

2.571429

25.7143%

表5 400*400大小矩阵乘法实验结果

线程数

时间

加速比

效率

1

0.139000

1

100%

2

0.087000

1.597701

79.8851%

4

0.038000

3.657895

91.4474%

8

0.032000

4.343750

54.2969%

表6 800*800大小矩阵乘法实验结果

线程数

时间

加速比

效率

1

0.494000

1

100%

2

0.322000

1.534161

76.7081%

4

0.144000

3.430556

85.7639%

8

0.115000

4.295652

53.6957%

  从上述表格中我们可以看到,随着问题规模的增大,无论是单线程即串行程序,还是2线程、4线程乃至8线程的计算时间都将增大。对于同等规模的问题,多线程的表现性能依然和此问题的计算规模有关系,当每一个子线程的计算开销小于线程调度等开销时,其加速比和效率往往表现不佳。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张小殊.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值