0

我对openCL完全陌生。我试图将顺序 3d 矩阵代码转换为 3d 矩阵的 openCL 版本。我已经实现了 openCL 的基本功能,但被困在 OpenCL 内核中。我得到了 3d 矩阵的所有索引,但不明白如何访问不同维度的索引。任何人都可以帮助我吗?

也欢迎您向我提出解决此问题的更好方法。提前致谢。

这是我的代码的一小部分。

顺序代码:

const int depth = 3;
const int row = 4;
const int column = 4;
float A[depth][row][column];

for (int k = 0; k < depth; k++){
    for (int i = 0; i < row; i++){
        for (int j = 0; j < column; j++){
            if (k == 0){
                A[k][i][j] = (float)i / ((float)j + 1.00);
            }else if (k == 1){
                A[k][i][j] = 1.00;
            }else{
                A[k][i][j] = (float)j / ((float)i + 1.00);
            }
        }
    }
 }

OpenCL 内核代码:

__kernel void ThreeDimArray(__global float *const output1) {
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  const int z = get_global_id(2);
  const int max_x = get_global_size(0);
  const int max_y = get_global_size(1);
  const int max_z = get_global_size(2);
  
  const int idx = x * max_y * max_z + y * max_z + z;

  output1[idx] = 1.00;
};

顺序代码输出:

Baseline matrix k = 0
0.00    0.00    0.00    0.00 
1.00    0.50    0.33    0.25 
2.00    1.00    0.67    0.50
3.00    1.50    1.00    0.75

Baseline matrix k = 1
1.00    1.00    1.00    1.00
1.00    1.00    1.00    1.00
1.00    1.00    1.00    1.00
1.00    1.00    1.00    1.00

Baseline matrix k = 2
0.00    1.00    2.00    3.00
0.00    0.50    1.00    1.50
0.00    0.33    0.67    1.00
0.00    0.25    0.50    0.75

编辑: 如果我们想用其他索引值更新特定索引怎么办。 例如:

for (int t = 0; t < 24; t++){
        for (int i = 1; i < row; i++){
            for (int j = 0; j < column; j++){
                A[1][i][j] = A[1][i][j] + (1 / (sqrt(A[0][i + 1][j] + A[2][i - 1][j])));
            }
        }
    }

我试过这样(内核代码):

const int idk0 = 0 * row * column + i * column + j; 
const int idk1 = 1 * row * column + i * column + j; 
const int idk2 = 2 * row * column + i * column + j;

for (int t = 0; t < 24; t++) {
    A[idk1] = A[idk1] + (1 / (sqrt(A[idk0 + 1] + A[idk2 - 1])));
  }
4

1 回答 1

1

你已经拥有了你所需要的一切。完成的内核如下所示:

__kernel void ThreeDimArray(__global float* A) {
  const int k = get_global_id(0);
  const int i = get_global_id(1);
  const int j = get_global_id(2);
  //const int depth  = get_global_size(0); // unused here
  const int row    = get_global_size(1);
  const int column = get_global_size(2);
  
  const int idx = k * row * column + i * column + j; // linear index to access matrix A in 1D form

  if(k == 0) {
      A[idx] = (float)i / ((float)j + 1.00f);
  } else if(k == 1) {
      A[idx] = 1.00;
  } else {
      A[idx] = (float)j / ((float)i + 1.00f);
  }
};

编辑:为了在矩阵大小方面获得最佳性能和最佳灵活性,我建议仅对内核范围使用一维索引。此外,您还可以将不同的值写入一个矩阵地址。为此,您可以使用三元运算符 ( ?:)。

__kernel void ThreeDimArray(__global float* A, const int depth, const int row, const int column) {
  const int idx = get_global_id(0); // 1D kernel range is depth*row*column
  const int t=n%(column*row), j=t%column, i=t/column, k=n/(column*row);
  A[idx] = k==0 ? (float)i/((float)j+1.0f) : k==1 ? 1.0f : (float)j/((float)i+1.0f);
};

编辑 2:要仅更新少数精选值,您有 2 个选择:

  1. 在 CPU 上执行此操作并将矩阵从/复制到 GPU。这适用于 1 次初始化。
  2. 编写一个单独的内核并在内核中,在内核if(k!=0) return;顶部有一个类似的声明。然后只有线程k!=0会继续并向矩阵写入一些内容。如果这些特殊值依赖于相邻矩阵值(如A[0][i+1][j]),请确保这些相邻矩阵已经初始化(因此需要第二个单独的内核来初始化特殊值)。确保在相邻线程从其参考点读取该值时,不要将新值写入线程中的单元格i,j,k,否则无法清除线程i,j,ki,j+1,ki,j-1,ki,j+1,k读取旧值或更新值,因为确切的执行顺序是随机的(所谓的竞争条件)。如果您反复需要编写这些特殊的矩阵值并且 A 是一个非常大的矩阵,则这种方法很好。在这种方法中,您根本不需要将 A 从 CPU 复制到 GPU 并返回,如果 A 很大,这可能会非常慢。
于 2022-01-06T06:54:59.120 回答