openMp 基本和不同的输出
openMp basic and different outputs
我刚开始学习 openMP,作为并行编程的大多数新手,我正在编写一个简单的脚本来并行求解积分。以下是我写的代码:
num_chunks = 1000;
int threads_assigned = 0;
int num_threads = 10;// omp_get_num_threads();
sum_arr = (double *) malloc(sizeof(double) * num_threads);
memset(sum_arr, 0, num_threads);
int sums_each_thread = num_chunks / num_threads;
printf("num of threads = %d\n", num_threads);
sum = 0.0;
omp_set_num_threads(num_threads);
chunk_size = 1.0/(double) num_chunks;
double start_time = omp_get_wtime();
#pragma omp parallel
{
int idx = omp_get_thread_num();
if (idx == 0) threads_assigned = omp_get_num_threads();
int sums_count;
int loop_size = idx * sums_each_thread;
for (sums_count = loop_size; sums_count < loop_size + sums_each_thread; sums_count++) {
printf("starting thread no %d\n", idx);
// double x = (idx * chunk_size) + (chunk_size / 2.0); simplified version in the next line
double x = chunk_size * (sums_count + 0.5);
double y = 4.0 / (1 + x*x);
sum_arr[idx - 1] += (y * chunk_size);
sum += y;
}
printf("ending thread no %d\n", idx);
}
double end_time = omp_get_wtime();
printf("sum = %f, in time %f(s)\n", sum * chunk_size, end_time - start_time);
int new_idx = 0;
sum = 0.0;
for(new_idx = 0; new_idx < num_threads; new_idx++) {
sum += sum_arr[new_idx];
}
printf("arr sum = %f, with total assigned threads = %d\n", sum, threads_assigned);
我通过以下方式计算面积:
1 ) 计算矩形各面积之和
2 ) 计算所有高度的总和(标量和),然后乘以 chunk_size.
从数学上讲,这两种方法都会产生相同的结果,但在上述代码的这种情况下,方法 num 2 总是显示正确的结果,而方法 num 1 失败。
谁能解释一下原因?
您的代码中有两个错误:
一个正在访问 arr_sum
中的无效条目,其中元素 -1
被主线程访问。
替换
sum_arr[idx - 1] += (y * chunk_size);
和
sum_arr[idx] += (y * chunk_size);
另一个问题是修改sum
没有同步。
获取先前的 sum
并存储更新后的值可以与其他线程的类似操作交错。这 将 破坏结果。从内部循环中删除 printf("starting thread no %d\n", idx);
后,应该可以观察到这种效果。要修复它,只需使用原子更新。
#pragma omp atomic
sum += y;
现在两种方式计算的总和都可以了。
但是,该程序的扩展性很差。一个原因是 arr_sum
上的“错误共享”,当线程访问同一行缓存时导致 CPU 级别的同步代价高昂。
另一个问题是在关键 sum += y
中使用原子指令,它遇到了同样的问题。
可能您需要的是使用 OpenMP 的缩减功能:
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < num_chunks; ++i) {
double x = chunk_size * (i + 0.5);
double y = 4.0 / (1 + x*x);
sum += y;
}
现在它可以完美扩展,并且比 OP 问题中更正后的循环快许多 (~100) 倍。它是如此之快,以至于需要 num_chunks > 10e7
才能使循环成为程序执行时间的重要部分。
我刚开始学习 openMP,作为并行编程的大多数新手,我正在编写一个简单的脚本来并行求解积分。以下是我写的代码:
num_chunks = 1000;
int threads_assigned = 0;
int num_threads = 10;// omp_get_num_threads();
sum_arr = (double *) malloc(sizeof(double) * num_threads);
memset(sum_arr, 0, num_threads);
int sums_each_thread = num_chunks / num_threads;
printf("num of threads = %d\n", num_threads);
sum = 0.0;
omp_set_num_threads(num_threads);
chunk_size = 1.0/(double) num_chunks;
double start_time = omp_get_wtime();
#pragma omp parallel
{
int idx = omp_get_thread_num();
if (idx == 0) threads_assigned = omp_get_num_threads();
int sums_count;
int loop_size = idx * sums_each_thread;
for (sums_count = loop_size; sums_count < loop_size + sums_each_thread; sums_count++) {
printf("starting thread no %d\n", idx);
// double x = (idx * chunk_size) + (chunk_size / 2.0); simplified version in the next line
double x = chunk_size * (sums_count + 0.5);
double y = 4.0 / (1 + x*x);
sum_arr[idx - 1] += (y * chunk_size);
sum += y;
}
printf("ending thread no %d\n", idx);
}
double end_time = omp_get_wtime();
printf("sum = %f, in time %f(s)\n", sum * chunk_size, end_time - start_time);
int new_idx = 0;
sum = 0.0;
for(new_idx = 0; new_idx < num_threads; new_idx++) {
sum += sum_arr[new_idx];
}
printf("arr sum = %f, with total assigned threads = %d\n", sum, threads_assigned);
我通过以下方式计算面积:
1 ) 计算矩形各面积之和
2 ) 计算所有高度的总和(标量和),然后乘以 chunk_size.
从数学上讲,这两种方法都会产生相同的结果,但在上述代码的这种情况下,方法 num 2 总是显示正确的结果,而方法 num 1 失败。
谁能解释一下原因?
您的代码中有两个错误:
一个正在访问 arr_sum
中的无效条目,其中元素 -1
被主线程访问。
替换
sum_arr[idx - 1] += (y * chunk_size);
和
sum_arr[idx] += (y * chunk_size);
另一个问题是修改sum
没有同步。
获取先前的 sum
并存储更新后的值可以与其他线程的类似操作交错。这 将 破坏结果。从内部循环中删除 printf("starting thread no %d\n", idx);
后,应该可以观察到这种效果。要修复它,只需使用原子更新。
#pragma omp atomic
sum += y;
现在两种方式计算的总和都可以了。
但是,该程序的扩展性很差。一个原因是 arr_sum
上的“错误共享”,当线程访问同一行缓存时导致 CPU 级别的同步代价高昂。
另一个问题是在关键 sum += y
中使用原子指令,它遇到了同样的问题。
可能您需要的是使用 OpenMP 的缩减功能:
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < num_chunks; ++i) {
double x = chunk_size * (i + 0.5);
double y = 4.0 / (1 + x*x);
sum += y;
}
现在它可以完美扩展,并且比 OP 问题中更正后的循环快许多 (~100) 倍。它是如此之快,以至于需要 num_chunks > 10e7
才能使循环成为程序执行时间的重要部分。