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

kewlgrl發表於2016-11-19

目錄

1.問題描述...1

2.演算法設計...1

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

2.2 並行演算法設計... 2

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

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

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

3.2 執行結果截圖... 4

3.3 實驗加速比分析... 5

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

3.4.1錯誤程式碼...5

3.4.2後果...6

3.4.3正確程式碼...6

3.4.4分析...6

 

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 並行演算法設計

並行演算法步驟如下

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

②“#pragmaomp parallel for”

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

“#pragmaomp parallel for”

③令數k=2;

“#pragmaomp critical”

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

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

⑥若sum = i,++ans;

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

⑧函式返回答案值ans。

 

2.3 理論加速比分析

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

本機測試中,p=4:若n=10000,則S=3.9981;若n=100000,則S=3.9998。

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

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

/*問題描述:一個數如果恰好等於它的因子之和,這個數就稱為“完數”。例如6=1+2+3,再如8的因子和是7(即1+2+4),8不是完數。程式設計找出1000以內的所有完數。
*/
//test.cpp : 定義控制檯應用程式的入口點。
//
 
#include "stdafx.h"
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <ctime>
#include <omp.h>
#include <windows.h>
using namespace std;
 
int OMP_Perfect(long long n)       //平行計算n以內的完數個數
{
    int ans = 0;                 // n以內的完數個數
    long long i, k;                      //迴圈變數
#pragma omp parallel for
    for (i = 2; i <= n;i++)  //1不算入一個完數。
    {
       long long sum = 1;                //數的因數之和,1是任何數的一個因數。
#pragma omp parallel for reduction(+:sum)              //解決sum資料衝突
       for (k = 2; k <= i / 2; k++)  //k<=i/2是要保證i%k能進行計算。
           if(i%k == 0)
              sum += k;       //因數之和
       if (sum == i)       //找到一個完數
           ++ans;                 //函式返回答案值ans
    }
    return ans;
}
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;                 //函式返回答案值ans
    }
    return ans;
}
int main()
{
    long long n;
    cin >> n;
    printf("%lld以內的完數有:\n",n);
    clock_t t1, t2;
    t1 = clock();
    printf("Sum= %d\n", OMP_Perfect(n)); //輸出平行計算出的n以內的完數個數
    t2 = clock();
    printf("parallel time: %d\n", t2 - t1);//輸出平行計算時間
    t1 = clock();
    printf("Sum= %d\n", Perfect(n)); //輸出序列計算出的n以內的完數個數
    t2 = clock();
    printf("serail time: %d\n", t2 - t1); //輸出序列計算時間
    system("pause");
    return 0;
}

3.2 執行結果截圖

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


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

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


3.3 實驗加速比分析

若n=10000,S=Ts/Tp=3.7857;若n=100000,S=Ts/Tp=3.7420。

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

3.4 遇到的問題及解決方案

3.4.1錯誤程式碼

int OMP_Perfect(longlong n)

{

    int ans = 0;                 

    long long i, k;                     

#pragma omp parallel for

    for (i = 2; i <= n;i++) 

    {

       long long sum = 1;                

#pragma omp parallel for

       for (k = 2; k <= i / 2; k++) 

           if(i%k == 0)

              sum += k;

       if (sum == i) ++ans;

    }

    return ans;

}

3.4.2後果

n以內的完數數量答案錯誤。

3.4.3正確程式碼

int OMP_Perfect(longlong n)       //平行計算n以內的完數個數

{

    int ans = 0;                  // n以內的完數個數

    long long i, k;                      //迴圈變數

#pragma omp parallel for

    for (i = 2; i <= n;i++)  //1不算入一個完數。

    {

       long long sum = 1;                //數的因數之和,1是任何數的一個因數。

#pragma omp parallel for reduction(+:sum)              //解決sum資料衝突

       for (k = 2; k <= i / 2; k++) //k<=i/2是要保證i%k能進行計算。

           if(i%k == 0)

              sum += k;       //因數之和

       if (sum == i)       //找到一個完數

           ++ans;                 //函式返回答案值ans

    }

    return ans;

}

3.4.4分析

由於是多執行緒執行,有超過兩個執行緒同時訪問一個記憶體區域,並且至少有一個執行緒的操作是寫操作,這樣就產生了資料競爭。如果不對資料競爭進行處理,結果會產生錯誤。

    第二個for語句並行使用“#pragma ompparallel for reduction(+:sum)”來解決sum的資料競爭問題。因為reduction子句為變數指定一個操作符,每個執行緒都會建立reduction變數的私有副本,在OpenMP區域結束處,將使用各個執行緒私有複製的值通過制定的操作符進行迭代運算,並賦值給原來的變數。

相關文章