如何在GPU上复制OpenACC中分配的向量指针内存的向量

时间:2018-12-19 23:14:17

标签: c++ stdvector openacc

我正在尝试在GPU上复制向量数组的向量。

我尝试使用OpenACC copyin子句。 copyin子句不会复制数组中的所有基础数​​据。当我尝试访问基础矢量数据时,在运行时出现“非法地址访问”错误。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="theme-color" content="#000000" />
        <link
            href="/assets/css/styles.css"
            rel="stylesheet"
            type="text/css"
        />
        <title>Site</title>
    </head>
    <body>
        <div id="root"></div>
        <script src="http://localhost:3312/bundle.js"></script>
    </body>
</html>

我想访问基础向量k1p [i]的元素,但是实际上此代码是使用pgi编译器编译的,但是当我运行此代码时,我得到了

  

对cuStreamSynchronize的调用返回了错误700:内核执行期间的非法地址

2 个答案:

答案 0 :(得分:0)

OpenACC数据子句仅执行对象的浅表副本。因为“向量”是三个指针的集合,所以这意味着将向量放在copyin子句中只会复制指针,而不是它指向的数据。

假设您正在使用PGI,最简单的方法是使用CUDA统一内存(即添加标志“ -ta = tesla:managed”),并让CUDA运行时为您管理数据移动。

否则,您需要执行向量的手动深层复制。这可能有点棘手,尤其是对于矢量而言,所以请告诉我您是否需要一个示例。

答案 1 :(得分:0)

同样,我强烈建议在设备上使用C ++向量时使用CUDA统一内存(托管)。向量是具有三个专用指针的容器类型。给定一个OpenACC复制或更新会进行浅表复制,将向量放置在数据区域中时,您将复制指针,而不是指针指向的数据。更糟糕的是,由于指针是私有的,因此无法使用设备指针进行更新。

相反,您将需要创建一个临时指针数组来隐藏矢量数组的数据,然后在设备上使用此temp变量。类似于以下内容:

% cat vector.data.cpp
#include <iostream>
#include <vector>
#include <cstdint>

using namespace std;

int main() {
   const int bin_num = 1024;
   long sum = 0;

   vector<int32_t> *k1p = new vector<int32_t>[bin_num];
   for (int i = 0; i < bin_num; i++) {
       k1p[i].push_back(i);
   }

   int32_t ** temp = new int32_t*[bin_num];
   int * sizes = new int[bin_num];
   #pragma acc enter data create(temp[0:bin_num][0:0])
   for (int i = 0; i < bin_num; i++) {
      int sze = k1p[i].size();
      sizes[i] = sze;
      temp[i] = k1p[i].data();
      #pragma acc enter data copyin(temp[i:1][:sze])
   }
   #pragma acc enter data copyin(sizes[:bin_num])

  #pragma acc parallel loop gang vector reduction(+:sum) present(temp,sizes)
  for (int i = 0; i < bin_num; i++) {
     for (int j=0; j< sizes[i]; ++j) {
         sum += temp[i][j];
     }
  }
  cout << "Sum: " << sum << endl;
  #pragma acc exit data delete(sizes)
  #pragma acc exit data delete(temp)
  delete [] sizes;
  delete [] k1p;
}

% pgc++ vector.data.cpp --c++11 -ta=tesla  -V18.10
% a.out
Sum: 523776

使用托管内存时,地址位于主机和设备均可访问的统一内存空间中。因此,当您访问“开始”和“结束”时,它们在设备上返回的地址是有效的。例如:

% cat vector.cpp
#include <iostream>
#include <vector>
#include <cstdint>
using namespace std;

int main() {
   const int bin_num = 1024;
   long sum = 0;
   vector<int32_t>::const_iterator i2_it;

   vector<int32_t> *k1p = new vector<int32_t>[bin_num];
   for (int i = 0; i < bin_num; i++) {
       k1p[i].push_back(i);
   }

#pragma acc parallel loop reduction(+:sum) private(i2_it)
  for (int i = 0; i < bin_num; i++) {
    for (i2_it=k1p[i].begin(); i2_it!=k1p[i].end(); i2_it++) {
         sum += *i2_it;
     }
  }
  cout << "Sum: " << sum << endl;
}

% pgc++ vector.cpp --c++11 -ta=tesla:managed -V18.10
% a.out
Sum: 523776

另一种替代方法是编写自己的类矢量。在Parallel Programming with OpenACC的第5章中,我写了一个有关如何执行此操作的基本示例。可以在https://github.com/rmfarber/ParallelProgrammingWithOpenACC

找到代码示例