最近入了并行计算的坑,也是因为这学期有一门这样的课。但是还没开始上就自己瞎看书。昨天下午开始配mpi,一开始想弄到clion里面,但是相关的资料太少了,遂卒。老老实实弄到了vs里面(虽然他很大很丑但是做这种事还是蛮方便)。但是今天早上打开越看越丑,难以接受,就跑到Ubuntu上面搞clion。目前依旧卒着,看看明天有心情继续配吧,今天先用vs跑一跑简单例子。

具体配置过程就不说了,vs上的有一大堆。

#include <stdio.h>
#include <string.h>
#include <mpi.h>

const int MAX_STRING = 100;

int main() {
    char greeting[MAX_STRING];
    int comm_sz;
    int my_rank;

    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    if (my_rank != 0) {
        sprintf(greeting, "Greeting from process %d of %d!", my_rank, comm_sz);
        MPI_Send(greeting, strlen(greeting) + 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
    }
    else {
        printf("I am the boos! (%d)\n", my_rank);
        for (int q = 1; q < comm_sz; ++q) {
            MPI_Recv(greeting, MAX_STRING, MPI_CHAR, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            printf("%s\n", greeting);
        }
    }
    MPI_Finalize();
    return 0;
}

简单做一下笔记,mpi就是消息传递接口的意思,简单来说就是开好几个进程,各自干各自的事情,然后定义了一套消息交流的方案,就实现了比较简单的并行。

程序一开始进行初始化,一个作用就是定义了由用户启动的所有进程所组成的通信子。然后两个函数声明了总的进程数量和自己的rank。

然后就是比较重要的通信了,两个函数

MPI_Send(sned_p,sned_size,type,to,tag,comm)

MPI_Recv(recv_p,recv_size,type,from,tag,comm,&status)

分别表示发送(接收)内容的起始位置,大小,传递的数据类型,发给谁(从谁那里收到),标记,通信子,(发送者信息)。

然后是一点应用:求函数的定积分(这里找的函数是2x^3-10*x^2+100)

#include <stdio.h>
#include <string.h>
#include <mpi.h>
#define db double
const db dir = 1e-7;
int comm_sz;
int my_rank;
db l, r, d;
db F(db x) {
    return 2 * x * x * x - 10 * x * x + 100;
}
db Cal(db L, db R) {
    db res = 0;
    for (db i = L; i <= R - dir; i += dir)
        res += (F(i) + F(i + dir));
    return res * dir / 2;
}int main() {
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    l = 0; r = 50;
    d = (r - l) / comm_sz;
    db L = l + d * my_rank, R = l + d * (my_rank + 1.0);
    db S = Cal(L, R);
    db ans = S;
    if (my_rank != 0) {
        MPI_Send(&S, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
    }
    else {
        for (int q = 1; q < comm_sz; ++q) {
            MPI_Recv(&S, 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            ans += S;
        }
        printf("The totle area is %.5f\n", ans);
    }
    MPI_Finalize();
    return 0;
}

自己瞎写的矩阵乘优化,开4个进程大约提速3倍的样子,但是空间浪费了不少,感觉还能优化好多好多,先沾上吧,去看看书上怎么写的。

#include <stdio.h>
#include <string.h>
#include <mpi.h>
#include <math.h>
#define maxn 5010
#define db double
const db dir = 1e-5;
int comm_sz;
int my_rank;
int n, a[maxn][maxn], b[maxn][maxn], c[maxn][maxn], d;
int min(int x, int y) {
    return x < y ? x : y;
}
void Cal(int l, int r, int size, int ans[maxn * maxn]) {
    if (l > r)return;
    //printf("%d %d\n", l, r);
    for (int k = 0; k < n; k++)
        for (int i = 0; i < size; i++)
            for (int j = 0; j < n; j++) {
                ans[i * n + j] += a[i + l][k] * b[k][j];
            }
}
int res[maxn*maxn], ans[maxn][maxn];
int main() {
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &comm_sz);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    n = 2000;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            a[i][j] = (i * 100 + j * 10) % 233;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            b[i][j] = (i * 100 + j * 10) % 233;
    d = ceil(n * 1.0 / comm_sz);
    int l = my_rank * d;
    int r = min(n - 1, l + d - 1);
    int size = r - l + 1;
    for (int i = 0; i < size; i++)
        for (int j = 0; j < n; j++)
            res[i * n + j] = 0;
    Cal(l, r, size, res);
    if (my_rank != 0) {
        if(size>0)MPI_Send(res, size * n, MPI_INT, 0, 0, MPI_COMM_WORLD);
    }
    else {
        int l, r, size, now = 0;
        l = 0; r = min(n - 1, d - 1);
        for (int i = l; i <= r; i++)
            for (int j = 0; j < n; j++) {
                ans[i][j] = res[now]; now++;
            }
        for (int q = 1; q < comm_sz; ++q) {
            l = q * d;
            r = min(n - 1, l + d - 1);
            if (l > r)break;
            size = r - l + 1;
            MPI_Recv(res, size * n, MPI_INT, q, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            now = 0;
            for (int i = l; i <= r; i++)
                for (int j = 0; j < n; j++) {
                    ans[i][j] = res[now]; now++;
                }
        }
        printf("ok\n");
        for (int i = 0; i < n; i++)
            printf("%d\n", ans[0][i]);
        /*
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++)
                printf("%d ", ans[i][j]);
            printf("\n");
        }*/

    }
    MPI_Finalize();
    return 0;
}