完數的MPI並行程式設計-平行計算

kewlgrl發表於2016-11-19

目錄

1.問題描述... 2

2.演算法設計... 2

2.1 序列演算法設計... 2

2.2 MPI訊息傳遞並行演算法設計... 3

2.3 理論加速比分析... 3

3.基於MPI的並行演算法實現... 3

3.1 程式碼及註釋... 3

3.2 單機執行... 6

3.2.1 執行結果截圖... 6

3.2.2 實驗加速比分析... 7

3.3 多機執行... 7

3.3.1 程式執行說明... 7

3.3.2 執行結果截圖... 8

3.3.3 實驗加速比分析... 9

3.4 遇到的問題及解決方案... 9

3.4.1錯誤程式碼... 9

3.4.2後果... 10

3.4.3正確程式碼... 10

3.4.4分析... 10

 

1.問題描述

一個數如果恰好等於它的因子之和,這個數就稱為“完數”。例如6=1+2+3,再如8的因子和是7(即1+2+4),8不是完數。輸入一個數n,程式設計找出n以內的所有完數及其個數。

2.演算法設計

2.1 序列演算法設計

序列演算法步驟如下:

①因為1不算入一個完數,所以令數i=2,初始化答案值ans=0;

②因為1是任何數的一個因數,所以可初始化因數之和sum=1;

③令數k=2;

④如果數i能整除數k,說明k是i的一個因數,則sum += k;

⑤若k<=i/2(控制範圍保證i%k能進行計算),++k,轉到④,否則轉到⑥;

⑥若sum = i,++ans;

⑦若i <=n,++i,轉到②,否則轉到⑧;

⑧函式返回答案值ans。

2.2 MPI訊息傳遞並行演算法設計

並行演算法步驟如下:

①初始化MPI執行環境,程式號為零時輸入n,隨後將 n 值廣播出去;②中間計算步驟與序列計算步驟相同;

③暫存各個程式平行計算得到的部分和,通過MPI_SUM函式來彙總資料,各個程式全部執行完畢即得最終計算答案。

 

2.3 理論加速比分析

若p個處理器上資料量為n,則S=np/(n+2plogp)。

本機測試中,p=2:若n=10000,則S=1.99976;若n=100000,則S=1.99998。

3.基於MPI的並行演算法實現

3.1 程式碼及註釋(變數名 名字首字母 開頭)

/*問題描述:一個數如果恰好等於它的因子之和,這個數就稱為“完數”。例如6=1+2+3,再如8的因子和是7(即1+2+4),8不是完數。程式設計找出1000以內的所有完數。
*/
//test.cpp : 定義控制檯應用程式的入口點。
//
 
#include "stdafx.h"
#include "mpi.h"
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <ctime>
#include <omp.h>
#include <windows.h>
using namespacestd;
 
int Perfect(long long n) //普通的序列演算法
{
    int ans = 0;                  // n以內的完數個數
    long long i,k;                      //迴圈變數
    for (i = 2; i <= n; i++)  //1不算入一個完數。
    {
        long long sum = 1;                //數的因數之和,1是任何數的一個因數。
        for (k= 2; k <= i / 2; k++)  //k<=i/2是要保證i%k能進行計算。
            if(i%k == 0)
                sum += k;       //因數之和
        if(sum == i)       //找到一個完數
        {
            ++ans;
            printf("%lld\n", sum);
        }              //函式返回答案值ans
    }
    return ans;
}
int main(int argc, char *argv[])
{
    long long n,temp;
    long long i,k;  //迴圈變數
    int done = 0, myid, numprocs;
    long longmypi, pi, ans;// n以內的完數個數
    double startwtime, endwtime;
    int namelen;
    charprocessor_name[MPI_MAX_PROCESSOR_NAME];
    MPI_Init(&argc,&argv);//初始化
    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);//標示相應程式組中有多少個程式
    printf("numprocs= %d\n", numprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);//標示各個MPI程式,告訴呼叫該函式程式的當前程式號
    MPI_Get_processor_name(processor_name,&namelen);//獲取當前程式執行的機器名稱
    fprintf(stderr, "Process %d on %s\n", myid, processor_name);
    fflush(stderr);
    while (!done)
    {
        if(myid == 0)
        {
            printf("輸入一個數字不超過: (0 退出) ");
            fflush(stdout);
            scanf_s("%lld",&n);
            temp = n;
            startwtime = MPI_Wtime();
            printf("%lld以內的完數有:\n",n);
        }
        /***************MPI平行計算n以內的完數個數*********************/
        MPI_Bcast(&n, 1, MPI_LONG, 0,MPI_COMM_WORLD);//將 n 值廣播出去
        if (n== 0)  done = 1;
        else
        {
            ans = 0;
            for (i= myid + 2; i <= n; i += numprocs) //1不算入一個完數。
            {
                long long sum = 1;                //數的因數之和,1是任何數的一個因數。
                for (k= 2; k <= i / 2; k++)  //k<=i/2是要保證i%k能進行計算。
                    if(i%k == 0)
                        sum += k;       //因數之和
                if(sum == i)       //找到一個完數
                {
                    ++ans;       //函式返回答案值ans
                    printf("%lld\n", sum);
                }
            }
            mypi = ans;//各個程式平行計算得到的部分和
            MPI_Reduce(&mypi, &pi, 1,MPI_LONG, MPI_SUM, 0, MPI_COMM_WORLD);//每個程式從sendBuf向root程式的receiveBuf發資料,通過opration函式(例如MPI_SUM)來彙總資料
            if(myid == 0)
            {
                //執行累加的號程式將近似值列印出來
                printf("Sum= %d\n", pi);
                endwtime = MPI_Wtime();//返回自過去某一時刻呼叫時的時間間隔,以秒為單位
                printf("MPI時間= %f\n",endwtime - startwtime);
                /*****************序列計算時間***********************/
                startwtime = MPI_Wtime();
                printf("Sum= %d\n", Perfect(temp)); //輸出序列計算出的n以內的完數個數
                endwtime = MPI_Wtime();
                printf("Serail時間= %f\n",endwtime - startwtime);//輸出序列計算時間
                                                                /****************************************/
            }
        }
    }
    MPI_Finalize();
    system("pause");
    return 0;
}
 


3.2 單機執行

3.2.1 執行結果截圖

(1)小資料量驗證正確性的執行結果截圖(不考慮加速比)


(2)大資料量獲得較好加速比的執行結果截圖

(體現序列時間、並行時間和好的加速比)


3.2.2 實驗加速比分析

若n=10000,S=Ts/Tp=2.0045;若n=100000,S=Ts/Tp=1.9976。

實驗加速比與理論加速比相差不大,在誤差允許範圍內可認為結果正確。

3.3 多機執行

3.3.1 程式執行說明

    使用Dell20201與Dell20202兩臺機器組成雙核執行平臺,程式分配如圖所示。

3.3.2 執行結果截圖

(1)小資料量驗證正確性的執行結果截圖(不考慮加速比)


 

(2)大資料量獲得較好加速比的執行結果截圖

(體現序列時間、並行時間和好的加速比)

3.3.3 實驗加速比分析

若n=10000,S=Ts/Tp=2.0027;若n=100000,S=Ts/Tp=2.000484。

實驗加速比與理論加速比相差不大,在誤差允許範圍內可認為結果正確。

3.4 遇到的問題及解決方案

3.4.1錯誤程式碼

long long ans;

double mypi, pi;

(此處略中間程式碼)

mypi =ans;//各個程式平行計算得到的部分和

MPI_Reduce(&mypi,&pi, 1, MPI_LONG, MPI_SUM, 0, MPI_COMM_WORLD);

3.4.2後果

通過MPI_SUM函式來彙總的資料pi錯誤,輸出為零。

3.4.3正確程式碼

long long mypi, pi, ans;//ansn以內的完數個數

(此處略中間程式碼)

mypi =ans;//各個程式平行計算得到的部分和

MPI_Reduce(&mypi,&pi, 1, MPI_LONG, MPI_SUM, 0, MPI_COMM_WORLD);

 

3.4.4分析

    相互賦值的兩個變數的資料型別必須相同,不然無法完成賦值,始終為預設值零。

相關文章