1、實驗目的
(1)學會將串行程序改為並行程序。
(2)學會mpich2的使用。
(3)學會openmp的配置。
(4)mpi與openmp之間的比較。
2、實驗要求(1)將串行冒泡程序局部並行化,以降低時間消耗。
(2) 理論上求出時間復雜度之比,根據結果得出時間消耗之比,進行比對分析。
二、實驗設備(環境)及要求Vs2013,mpich2
三、實驗內容與步驟
1、實驗一 mpi並行
(1)實驗內容
1、寫出一個冒泡排序程序,求出其時間復雜度,並運行得到相應的時間消耗。
2、將冒泡程序改為mpi並行程序:將全部需要排序的數分成4等份,分給四個進程一起冒泡,最后將所得的結果歸到一個進程,進行歸並排序,得到結果,得到時間消耗。算出時間復雜度。
3、對得出的結果進行討論與分析。
(2)主要步驟
1)串行冒泡程序
時間復雜度:取所要排序的數的個數為n個,時間復雜度為n*n/2。
代碼實現:
#include "stdafx.h"
#include "stdlib.h"
#include"time.h"
const int ARRAY_SIZE = 120000;
int main(int argc, char* argv[])
{
int zongshu[ARRAY_SIZE];
srand(10086);
time_t now_time, end_time;
for (int i = 0; i < ARRAY_SIZE; i++){
zongshu[i]=rand();
}
now_time = time(NULL);
for (int i = 0; i < ARRAY_SIZE; i++)
{
for (int j = ARRAY_SIZE - 1; j > i; j--)
{
if (zongshu[j] <= zongshu[j - 1])
{
int z = zongshu[j - 1];
zongshu[j - 1] = zongshu[j];
zongshu[j] = z;
}
}
}
end_time = time(NULL);
long shijian = end_time - now_time;
for (int i = 0; i <ARRAY_SIZE; i++){
printf("%d ", zongshu[i]);
}
printf("所用時間:%ld",shijian);
while (true);
}
2)並行程序
時間復雜度:取所要排序的數的個數為n個,進程數為m個。時間復雜度:((n/m)*(n/m)/2)+n+4*n。
代碼實現:
// MPITest.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "mpi.h"
#include <stdio.h>
#include <math.h>
#include "stdlib.h"
#define SIZE 4//進程數
const int ARRAY_SIZE = 30000;//每個進程分配的個數
int shuzu[SIZE][ARRAY_SIZE];
int zonghanshu[SIZE][ARRAY_SIZE];
double endwtime;
void Scatter_1(int);
int main(int argc, char *argv[]){
int myid;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
Scatter_1(myid);
MPI_Finalize();
}
void Scatter_1(int myid){
int numtasks;
srand(10086);
for (int i = 0; i < SIZE; i++){
for (int j = 0; j < ARRAY_SIZE; j++){
shuzu[i][j] = rand();
}
}
//隨機生成數組
int xiaopaixu[ARRAY_SIZE];
double startwtime = MPI_Wtime();
MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
if (numtasks == SIZE){
MPI_Scatter(shuzu, ARRAY_SIZE, MPI_INT, xiaopaixu, ARRAY_SIZE, MPI_INT, 0, MPI_COMM_WORLD);
for (int i = 0; i <ARRAY_SIZE; i++){
for (int j = ARRAY_SIZE - 1; j > i; j--){
if (xiaopaixu[j] <= xiaopaixu[j - 1]){
int z = xiaopaixu[j - 1];
xiaopaixu[j - 1] = xiaopaixu[j];
xiaopaixu[j] = z;
}
}
}//每個進程里的冒泡排序
MPI_Gather(xiaopaixu, ARRAY_SIZE, MPI_INT, zonghanshu, ARRAY_SIZE, MPI_INT, 0, MPI_COMM_WORLD);
int time[SIZE];
for (int i = 0; i < SIZE; i++){
time[i] = 0;
}
int a[SIZE];
int zongpaixu2[ARRAY_SIZE*SIZE];
for (int j = ARRAY_SIZE*SIZE - 1; j >= 0; j--){
for (int k = 0; k < SIZE; k++){
if (time[k] >= ARRAY_SIZE){
a[k] = 0;
}
else
{
a[k] = zonghanshu[k][ARRAY_SIZE - time[k] - 1];
}
}
int x = a[0];
for (int i = 1; i<SIZE; i++){
if (a[i]>x){
x = a[i];
}
}
for (int n = 0; n < SIZE; n++){
if (x == a[n]){
time[n] = time[n] + 1;
break;
}
}
zongpaixu2[j] = x;
}
endwtime = MPI_Wtime();
if (myid);
else
for (int i = 0; i < SIZE*ARRAY_SIZE; i++){
printf("%d ", zongpaixu2[i]);
}
}
if (myid);
else
printf("wall clock time=% f\n", endwtime - startwtime);
}
2、實驗2
在實驗一的基礎上將程序改為openmp。
代碼實現:(水平不高,寫的程序通用性不好,只寫了四線程的)
#include "stdafx.h"
#include <stdio.h>
#include <math.h>
#include "stdlib.h"
#include"time.h"
#include <omp.h>
#define SIZE 4
const int ARRAY_SIZE = 12000;
int shuzu[SIZE][ARRAY_SIZE];
int xiaopaixu1[ARRAY_SIZE];
int xiaopaixu2[ARRAY_SIZE];
int xiaopaixu3[ARRAY_SIZE];
int xiaopaixu4[ARRAY_SIZE];
int zonghanshu[SIZE][ARRAY_SIZE];
int zongpaixu[ARRAY_SIZE*SIZE];
void xiaohansu(int *A, int l, int u){
for (int i = l; i <u; i++){
for (int j = u - 1; j > i; j--){
if (A[j] <= A[j - 1]){
int z = A[j - 1];
A[j - 1] = A[j];
A[j] = z;
}
}
}
}
//每個線程排序
int main(int argc, char* argv[])
{
int t1, t2;
int i;
int id;
clock_t now_time, end_time;
srand(10086);
for (int i = 0; i < SIZE; i++){
for (int j = 0; j < ARRAY_SIZE; j++){
shuzu[i][j] = rand();
}
}
//隨機生成數組
now_time = clock();
#pragma omp parallel default(none) shared(shuzu,xiaopaixu1,xiaopaixu2,xiaopaixu3,xiaopaixu4,ARRAY_SIZE) private(i)
{
#pragma omp for
for (i = 0; i < ARRAY_SIZE; i++)//這個for循環是並行的,將數組分為四份
{
xiaopaixu1[i] = shuzu[0][i];
xiaopaixu2[i] = shuzu[1][i];
xiaopaixu3[i] = shuzu[2][i];
xiaopaixu4[i] = shuzu[3][i];
}
}
#pragma omp parallel default(none) shared(xiaopaixu1,xiaopaixu2,xiaopaixu3,xiaopaixu4,ARRAY_SIZE)
{
#pragma omp parallel sections
{
#pragma omp section
xiaohansu(xiaopaixu1, 0, ARRAY_SIZE-1);//排序
#pragma omp section
xiaohansu(xiaopaixu2, 0, ARRAY_SIZE);
#pragma omp section
xiaohansu(xiaopaixu3, 0, ARRAY_SIZE);
#pragma omp section
xiaohansu(xiaopaixu4, 0, ARRAY_SIZE);
}
}
for (i = 0; i < ARRAY_SIZE; i++)//合到一份
{
zonghanshu[0][i]=xiaopaixu1[i];
zonghanshu[1][i]=xiaopaixu2[i];
zonghanshu[2][i]=xiaopaixu3[i];
zonghanshu[3][i]=xiaopaixu4[i];
}
int time[SIZE];
for (int i = 0; i < SIZE; i++){
time[i] = 0;
}
int a[SIZE];
for (int j = ARRAY_SIZE*SIZE - 1; j >= 0; j--){
for (int k = 0; k < SIZE; k++){
if (time[k] >= ARRAY_SIZE){
a[k] = 0;
}
else
{
a[k] = zonghanshu[k][ARRAY_SIZE - time[k] - 1];
}
}
int x = a[0];
for (int i = 1; i<SIZE; i++){
if (a[i]>x){
x = a[i];
}
}
for (int n = 0; n < SIZE; n++){
if (x == a[n]){
time[n] = time[n] + 1;
break;
}
}
zongpaixu[j] = x;
}
//歸並
end_time = clock();
double shijian = end_time - now_time;
for (int i = 0; i <SIZE*ARRAY_SIZE; i++){
printf("%d ", zongpaixu[i]);
}
printf("所用時間:%lf", shijian / CLK_TCK);
while (true);
}
四:實驗結果與分析
Mpi:
串行
Mpi
|
|
1.2萬 |
2.4萬 |
3.6萬 |
4.8萬 |
6.0萬 |
7.2萬 |
| 串行(秒) |
0.441 |
1.766 |
3.951 |
6.877 |
10.469 |
14.687 |
| 6線(秒) |
0.029 |
0.108 |
0.242 |
0.435 |
0.656 |
0.940 |
| 4線(秒) |
0.035 |
0.151 |
0.339 |
0.615 |
0.969 |
1.409 |
| 2線(秒) |
0.119 |
0.502 |
1.108 |
2.040 |
3.121 |
4.516 |
從表中可以看出4線程的時候,並行程序的速度是串行程序速度的十倍之多,而理論上大概8倍。這就跟改的程序有關。在並行程序中,最后采用的是歸並,由此,發生了這些奇妙的情況:實則本身的算法就比冒泡優一些,但又不能只采用冒泡算法,那樣在最后又來個冒泡,其程序就沒有意義了。
Openmp:
這是4.8萬個數排序的結果,可以看出用了2.876秒,比MPI慢了四倍之多,這可能是程序的不合理,帶來了多余的時間消耗(通信)。但比串行還是要快很多。
五:結論(討論)
1、實驗結論
(1) 就這冒泡排序改為並行的,雖然時間縮短了很多倍,但與快排等排序算法並行相比,其速度又不堪入目。
(2) 就冒泡排序而言,其mpi並行遠遠優於openmp(就我寫的程序而言。。。),雖然最后都用了並歸。
2、討論
(1) 這些程序都實現在一台電腦上完成的,還未試過與其他電腦通信,所以其所表現出來的結果並不完全按正確,畢竟並行計算涉及到不同主機之間的通信。
(2) 由於個人編程能力不高,在這里只討論了一些時間上的差異,並未對空間上進行比對(不會。。。)。
(3)就openmp程序而言,應該還可以改寫,增加其通用性和減少通信。
