OpenMP(二)并行运算中的数据依赖问题

一. 数据竞争(data race)

在共享内存的情况下,如果两个线程同时对一个数据进行写操作,可能会导致数据竞争和不确定性问题。

比如线程1执行 x = 1, 线程2执行 x = 2。很难判断x的最终结果是什么,因为你不知道这两个线程谁先执行谁后执行。

如果在并行运算中,不同的线程出现了数据竞争的情况,或者两个线程之间有数据依赖关系,该如何解决呢?

example 1: 把变量私有化

这是一段简单的 数组a的元素 =(数组b的元素+数组c的元素)/ 2 的代码。在编写代码的过程中,大家习惯于使用一个temp变量存储临时结果。

float temp, a[n], b[n], c[n];
... // Initialise arrays b and c

int i;
for ( i =0; i < n; i++)
{
    temp = 0.5f * (b[i] + c[i]);
    a[i] = temp;
}

那么,在加入#pragma omp parallel for将for循环并行化以后

float temp, a[n], b[n], c[n];
... // Initialise arrays b and c

int i;
#pragma omp parallel for
for ( i =0; i < n; i++)
{
    temp = 0.5f * (b[i] + c[i]);
    a[i] = temp;
}

变量temp会被多个线程同时访问,造成数据竞争的状况。比如,线程A计算得到temp = 1以后,线程B计算得出temp = 2, 覆盖了A的结果,会导致线程A下的a[i]结果错误。

因此,需要将temp元素变为每个线程私有的。

float temp, a[n], b[n], c[n];
... // Initialise arrays b and c

int i;
#pragma omp parallel for private(temp)
for ( i =0; i < n; i++)
{
    temp = 0.5f * (b[i] + c[i]);
    a[i] = temp;
}

example 2: 提前备份数据

这是一段简单的将数组的每个元素前移一位的代码:

float a[n];
... // Initialise array a

int i;
#pragma omp parallel for
for ( i = 0; i < n-1; i++ )
    a[i] = a[i+1];

在并行执行的情况下,可能线程A先执行,更改了数组的元素,导致线程B开始执行以后访问的数组已经是被A更改过的结果了。

解决方案是事先把原始数组备份:

float atemp[n];

#pragma omp parallel for
for (i = 1; i < n; i++)
    atemp[i] = a[i];

#pragma omp parallel for
for (i = 0; i < n-1; i++)
    a [i] = atemp[i+1];

example 3: 奇偶元素分开操作

这是一段简单的冒泡排序代码:

void sortSet() {
    int i, j, temp;
    for (i = 0; i < setSize-1; i++) {
        for (j = 0; j < setSize-i-1; j++) {
            if (set[j] > set[j+1]) {
                    temp = set[j];
                    set[j] = set[j+1];
                    set[j+1] = temp;
            }
        }
    }
}

如果单纯在外层for循环加上#pragma omp parallel for来使其并行化的话,会出现排序结果混乱的问题。举例:

对数组[3,2,1]进行升序排序。

假设线程A判断set[0] > set[1],认为应该交换set[0] 和set[1]

线程B判断set[1] > set[2],认为应该交换,并率先进行,数组变成 [3,1,2]

A在B的基础上进行更改,交换set[0] 和set[1],数组变成 [1,3,2]

最后并没有达到理想的结果[1,2,3]。

解决方案:由于冒泡排序只有相邻的元素会互相交换,收到并行运算的影响,因此将其奇偶元素分开操作:

void sortSet()
{
    int i, j;
    int flag = 0;

    for (i = 0; i < setSize - 1 && !flag; i++)
    {
        flag = 1;

        #pragma omp parallel for
        for (j = 1; j < setSize - 1; j += 2)
        {
            if (set[j] > set[j + 1])
            {
                int temp = set[j];
                set[j] = set[j + 1];
                set[j + 1] = temp;
                flag = 0;
            }
        }

        #pragma omp parallel for
        for (j = 0; j < setSize - 1; j += 2)
        {
            if (set[j] > set[j + 1])
            {
                int temp = set[j];
                set[j] = set[j + 1];
                set[j + 1] = temp;
                flag = 0;
            }
        }
    }
}

其中flag的作用是标记是否在当前轮迭代中发生了交换。如果没有发生交换,则说明数组已经有序,此时可以提前结束排序。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
VTK是一个用于可视化和图形处理的开源库。它提供了很多用于创建、处理和呈现数据的工具和算法,包括可视化非结构化数据集的功能。在VTK使用OpenMP并行创建非结构化数据集可以提高效率,加速计算过程。 以下是使用OpenMP并行创建非结构化数据集的步骤: 1. 导入必要的头文件和命名空间。 ```c++ #include <vtkUnstructuredGrid.h> #include <vtkPoints.h> #include <vtkCellArray.h> #include <vtkSmartPointer.h> #include <vtkFloatArray.h> #include <vtkXMLUnstructuredGridWriter.h> #include <vtkXMLPUnstructuredGridWriter.h> #include <omp.h> using namespace std; ``` 2. 定义网格的参数,包括网格大小和网格分辨率。 ```c++ int nx = 100; int ny = 100; int nz = 100; int num_points = nx * ny * nz; ``` 3. 创建网格点的坐标数组,并使用OpenMP并行计算。 ```c++ vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); points->SetNumberOfPoints(num_points); #pragma omp parallel for for (int k = 0; k < nz; k++) { for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { double x = i / double(nx - 1); double y = j / double(ny - 1); double z = k / double(nz - 1); int index = k * ny * nx + j * nx + i; points->SetPoint(index, x, y, z); } } } ``` 4. 创建单元格数组,并使用OpenMP并行计算。 ```c++ vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New(); cells->Allocate(num_points); #pragma omp parallel for for (int k = 0; k < nz - 1; k++) { for (int j = 0; j < ny - 1; j++) { for (int i = 0; i < nx - 1; i++) { int id0 = k * ny * nx + j * nx + i; int id1 = id0 + 1; int id2 = id0 + nx; int id3 = id2 + 1; int id4 = id0 + nx * ny; int id5 = id4 + 1; int id6 = id4 + nx; int id7 = id6 + 1; vtkSmartPointer<vtkHexahedron> hex = vtkSmartPointer<vtkHexahedron>::New(); hex->GetPointIds()->SetId(0, id0); hex->GetPointIds()->SetId(1, id1); hex->GetPointIds()->SetId(2, id3); hex->GetPointIds()->SetId(3, id2); hex->GetPointIds()->SetId(4, id4); hex->GetPointIds()->SetId(5, id5); hex->GetPointIds()->SetId(6, id7); hex->GetPointIds()->SetId(7, id6); cells->InsertNextCell(hex); } } } ``` 5. 将网格数据写入文件。 ```c++ vtkSmartPointer<vtkUnstructuredGrid> grid = vtkSmartPointer<vtkUnstructuredGrid>::New(); grid->SetPoints(points); grid->SetCells(VTK_HEXAHEDRON, cells); vtkSmartPointer<vtkXMLUnstructuredGridWriter> writer = vtkSmartPointer<vtkXMLUnstructuredGridWriter>::New(); writer->SetFileName("grid.vtu"); writer->SetInputData(grid); writer->Write(); ``` 以上就是在VTK使用OpenMP并行创建非结构化数据集的步骤,通过并行计算可以显著提高计算效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值