一. 实验目的
1. 实现简单排序的OpenMP并行程序;
2. 掌握for编译制导语句;
3. 对并行程序进行简单的性能分析。
二. 实验环境
1. 软件环境:Microsoft Visual Studio 2013。
三. 实验内容
1. 实验要求:排序数组a:
l 数组大小n和线程数p都是可输入的参数。
l 数组a中的每个数都初始化为一个0到1之间的随机double型值(用rand()/double(RAND_MAX)实现)。
l 先将数组a分为p份,每个线程调用sort函数排序一份,然后调用merge函数将p个有序序列归并。
l 添加检测排序结果是否正确的代码。
l 添加计算排序时间的代码,注意不包含数组的初始化时间。
2. 程序代码和说明:
#include<iostream>
#include<algorithm>
#include <omp.h>
#include<vector>
using namespace std;
int n, p; //数组大小n和线程数p都是可输入的参数。
//判断数组是否有序
bool judge(double *a, double*c) {
for (int i = 0;i < n;i++) {
if (a[i] != c[i]) {
cout << "error!" << endl;
return false;
}
}
return true;
}
//借助temp 数组b来合并两段有序的数组a
void merge(double *a, double *b, int first, int middle, int endaddress)
{
int i = first, j = middle + 1, k = first;
while (i <= middle && j <= endaddress) {
if (a[i] > a[j]) {
b[k++] = a[j++];
}
else {
b[k++] = a[i++];
}
}
//合并剩下多余的
while (i <= middle) b[k++] = a[i++];
while (j <= endaddress) b[k++] = a[j++];
//重新赋值
for (i = first; i <= endaddress; i++) a[i] = b[i];
}
int main()
{
clock_t start, end;
//数组大小n和线程数p
cout << "请输入数组大小:" << endl;
cin >> n;
cout << "请输入线程数:" << endl;
cin >> p;
for (int pi = 0;pi < 7;pi++) {//每次线程数*2,循环七次
cout << "+++++++++++++++++++++++++++++++线程数为" << p << "时:++++++++++++++++++++++++" << endl;
//记录并行运行时间的数组
double multi_time[5];
//归并函数中用作辅助的数组temp
double* temp = new double[n];
memset(temp, 0, n);
for (int times = 0;times < 5;times++) {//循环五次
cout << "---------------------第" << times + 1 << "次并行计算:----------------------" << endl;
start = 0;end = 0;
//数组a中的每个数都初始化为一个0到1之间的随机double型值
double* a = new double[n];//a数组存储并行结果
double* b = new double[n];//b数组存储标准结果
omp_set_num_threads(p);//设置并行线程数
int i;
//数组初始化
for (i = 0;i < n;i++) {
a[i] = rand() / double(RAND_MAX);
b[i] = a[i];
}
sort(b, b + n);
//并行开始计时
start = clock();
#pragma omp parallel shared(a,temp) private(i)
{
//对数组分成p路排序
int num = n / p;
#pragma omp for schedule(static,1)
for (i = 0; i < p; i++) {
sort(a + i * num, a + (i + 1) * num);
}
}
//利用temp数组对部分排序的a数组进行归并
for (int block = n / p; block < n; block += block)
{//分块归并,每次循环扩大块一倍
for (int first = 0; first < n - block; first += (block + block))
{//确定块内数组first middle end 的address
int middle = first + block - 1;
int endaddress = first + block + block - 1;
if (endaddress >= n) {
endaddress = n - 1;
}
merge(a, temp, first, middle, endaddress);
}
}
end = clock();
//存储并行时间
multi_time[times] = (end - start) / 1000.0;
cout << "并行时间为" << multi_time[times] << endl;
//判断并行的结果数组是否有序
judge(a, b);
delete[]a;
delete[]b;
}
cout << "--------------------------------------串行计算---------------------------------" << endl;
double* c = new double[n];//串行测试数组
double* d = new double[n];//标准数组
memset(temp, 0, n);
//数组初始化
for (int i = 0;i < n;i++) {
c[i] = rand() / double(RAND_MAX);
d[i] = c[i];
}
sort(d, d + n);
//开始计时
start = clock();
//按照线程分开排序
for (int i = 0; i < p; i++) {
sort(c + i * (n / p), c + (i + 1) * (n / p));
}
//p路归并
for (int block = n / p; block < n; block += block)
{
for (int first = 0; first < n - block; first += (block + block))
{
int middle = first + block - 1;
int endaddress = first + block + block - 1;
if (endaddress >= n) {
endaddress = n - 1;
}
merge(c, temp, first, middle, endaddress);
}
}
end = clock();
//判断是否数组已经有序
judge(c, d);
double single_time = (end - start) / 1000.0;
cout << "串行消耗时间为:" << single_time << "秒" << endl;
//计算并行平均时间
double average;
for (int i = 0;i < 5;i++) {
average += multi_time[i];
}
average /= 5;
cout << "并行平均时间为:" << average << "秒" << endl;
cout << "平均加速比为" << single_time / average << endl;
p *= 2;
delete []c;
delete[]temp;
}
}
3. 实验结果和分析:测试并行程序在不同线程数下的执行时间和加速比(串行执行时间/并行执行时间),并分析实验结果。其中,数组大小n固定为100000000,线程数分别取1、2、4、8、16、32、64时,为减少误差,每项实验进行5次,取平均值作为实验结果。
表1 并行程序在不同线程数下的执行时间(秒)和加速比
线程数 执行时间 | 1 | 2 | 4 | 8 | 16 | 32 | 64 |
第1次 | 10.025 | 6.357 | 5.682 | 5.563 | 6.024 | 6.537 | 6.683 |
第2次 | 9.828 | 6.189 | 5.29 | 5.347 | 5.542 | 6.039 | 6.504 |
第3次 | 9.906 | 6.356 | 5.21 | 5.243 | 5.505 | 6.466 | 6.478 |
第4次 | 9.79 | 6.269 | 5.274 | 5.207 | 5.478 | 6.076 | 6.544 |
第5次 | 9.791 | 6.219 | 5.034 | 5.363 | 5.889 | 6.171 | 6.461 |
平均值 | 9.868 | 8.2516 | 6.94832 | 6.73426 | 7.03445 | 7.66469 | 8.06694 |
加速比 | 1.00466 | 1.25527 | 1.56498 | 1.71867 | 1.71442 | 1.65643 | 1.63668 |