我开始使用C++使用OpenMP.
我有两个问题:
#pragma omp for schedule?dynamic和之间有什么区别static?请用例子说明.
我想使用OpenMP并行填充直方图.我在C/C++中使用OpenMP提出了两种不同的方法.
第一种方法为每个线程proccess_data_v1创建一个私有直方图变量hist_private,并行填充它们,然后将私有直方图加到hist一个critical部分中的共享直方图中.
第二种方法生成proccess_data_v2一个直方图的共享数组,其数组大小等于线程数,并行填充此数组,然后并行地对共享直方图求和hist.
第二种方法似乎优于我,因为它避免了临界区并且并行地对直方图求和.但是,它需要知道线程数和调用omp_get_thread_num().我一般都试图避免这种情况.有没有更好的方法来执行第二种方法而不引用线程数并使用大小等于线程数的共享数组?
void proccess_data_v1(float *data, int *hist, const int n, const int nbins, float max) {
#pragma omp parallel
{
int *hist_private = new int[nbins];
for(int i=0; i<nbins; i++) hist_private[i] = 0;
#pragma omp for nowait
for(int i=0; i<n; i++) {
float x = reconstruct_data(data[i]);
fill_hist(hist_private, nbins, max, x);
}
#pragma omp critical
{
for(int i=0; i<nbins; i++) {
hist[i] += hist_private[i];
}
} …Run Code Online (Sandbox Code Playgroud) 我试图了解48核系统上的并行化限制(4xAMD Opteron 6348,2.8 Ghz,每个CPU 12个核心).我写了这个微小的OpenMP代码来测试加速,我认为这是最好的情况(任务是令人尴尬的并行):
// Compile with: gcc scaling.c -std=c99 -fopenmp -O3
#include <stdio.h>
#include <stdint.h>
int main(){
const uint64_t umin=1;
const uint64_t umax=10000000000LL;
double sum=0.;
#pragma omp parallel for reduction(+:sum)
for(uint64_t u=umin; u<umax; u++)
sum+=1./u/u;
printf("%e\n", sum);
}
Run Code Online (Sandbox Code Playgroud)
我惊讶地发现缩放是高度非线性的.使用48个线程运行代码需要大约2.9s,使用36个线程运行3.1s,使用24个线程运行3.7s,使用12个线程运行4.9s,使用1个线程运行代码需要57s.
不幸的是,我不得不说计算机上运行一个进程使用100%的一个核心,因此可能会影响它.这不是我的过程,所以我无法结束它来测试差异,但不知何故我怀疑这是在19到20倍的加速和理想的48倍加速之间产生差异.
为了确保它不是OpenMP问题,我同时运行了两个程序副本,每个程序有24个线程(一个用umin = 1,umax = 5000000000,另一个用umin = 5000000000,umax = 10000000000).在这种情况下,程序的两个副本在2.9s之后完成,因此它与使用单个程序实例运行48个线程完全相同.
什么阻止了这个简单程序的线性缩放?
据我所知,对于NUMA系统的性能,有两种情况需要避免:
一个简单的例子会有帮助.假设我有一个双插槽系统,每个插槽都有一个带有两个物理内核的CPU(和两个逻辑内核,即每个模块没有Intel超线程或AMD两个内核).让我借一下OpenMP的数据:按计划进行
| socket 0 | core 0 | thread 0 |
| | core 1 | thread 1 |
| socket 1 | core 2 | thread 2 |
| | core 3 | thread 3 |
Run Code Online (Sandbox Code Playgroud)
因此,基于案例1,最好避免例如线程0和线程1写入相同的高速缓存行,并且基于案例2,最好避免例如线程0写入与线程2相同的虚拟页面.
但是,我被告知在现代处理器上,第二种情况不再是一个问题.套接字之间的线程可以有效地写入同一个虚拟页面(只要它们不写入同一个缓存行).
案例二不再是问题吗?如果仍然存在问题,那么正确的术语是什么?将这两种情况称为虚假共享是否正确?
我需要实现归约操作(对于每个线程,值应该存储在不同的数组条目中)。但是,对于更多线程,它运行得更慢。有什么建议?
double local_sum[16];.
//Initializations....
#pragma omp parallel for shared(h,n,a) private(x, thread_id)
for (i = 1; i < n; i++) {
thread_id = omp_get_thread_num();
x = a + i* h;
local_sum[thread_id] += f(x);
}
Run Code Online (Sandbox Code Playgroud)