【问题标题】:How do I know if OpenCL kernel is working properly?我如何知道 OpenCL 内核是否正常工作?
【发布时间】:2021-08-31 01:05:51
【问题描述】:

我编写了简单的代码来使用 OpenC 求解平流方程,并将结果写入 netcdf 文件。该代码在编译期间不会产生任何错误消息,并且它运行时没有任何错误。但似乎内核没有做任何事情。内核将数值方案循环了大约 3000 次,如果它正常工作,我应该会看到一些非常不同的东西。有没有办法查明内核是否正常工作,比如打印?

下面是内核

void pbndry(int in_x_siz, int in_y_siz, int in_z_siz, global float *in_arr)
{
   int i,j,k;

   // Periodic boundary
   // x-direction
   for(k=1;k<in_z_siz+1;k++)
      for(j=1;j<in_y_siz+1;j++)
         {
         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + 0] =
         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + in_x_siz];

         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + (in_x_siz+1)] =
         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + 1];
         }

   // y-direction
   for(k=1;k<in_z_siz+1;k++)
      for(i=1;i<in_x_siz+1;i++)
         {
         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + 0 * (in_x_siz+2) + i] =
         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + in_y_siz * (in_x_siz+2) + i];

         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + (in_y_siz+1) * (in_x_siz+2) + i] =
         in_arr[k * (in_y_siz+2) * (in_x_siz+2) + 1 * (in_x_siz+2) + i];
         }

   // z-direction
   for(j=1;j<in_y_siz+1;j++)
      for(i=1;i<in_x_siz+1;i++)
         {
         in_arr[0 * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + i] =
         in_arr[in_z_siz * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + i];

         in_arr[(in_z_siz+1) * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + i] =
         in_arr[1 * (in_y_siz+2) * (in_x_siz+2) + j * (in_x_siz+2) + i];
         }
}


kernel void leapfrog3d(
                        const  int x_siz,
                        const  int y_siz,
                        const  int z_siz,
                        const  int t_siz,
                        global float *in_p_tf,
                        global float *in_p_tn,
                        global float *in_p_tp,
                        const  float u_vel,
                        const  float v_vel,
                        const  float w_vel,
                        const  float c,
                        global float *in_p_rs
                      )
{
   int nx   =  x_siz;
   int ny   =  y_siz;
   int nz   =  z_siz;
   int nt   =  t_siz;
   float u  =  u_vel;
   float v  =  v_vel;
   float w  =  w_vel;
   float C  =  c    ;
   int i    =  get_global_id(0);
   int j    =  get_global_id(1);
   int k    =  get_global_id(2);

   int idx0, idx_i0, idx_i1, idx_j0, idx_j1, idx_k0, idx_k1;


   for(int t=1;t<t_siz;t++)
   {

      idx0     =  i + j * (nx+2) + k * (nx+2) * (ny+2);

      idx_i0   =  (i+1) + j * (nx+2) + k * (nx+2) * (ny+2);
      idx_j0   =  i + (j+1) * (nx+2) + k * (nx+2) * (ny+2);
      idx_k0   =  i + j * (nx+2) + (k+1) * (nx+2) * (ny+2);
      
      idx_i1   =  (i-1) + j * (nx+2) + k * (nx+2) * (ny+2);
      idx_j1   =  i + (j-1) * (nx+2) + k * (nx+2) * (ny+2);
      idx_k1   =  i + j * (nx+2) + (k-1) * (nx+2) * (ny+2);

      in_p_tf[idx0] = in_p_tp[idx0] 
                   - u_vel * C * (in_p_tn[idx_i0] - in_p_tn[idx_i1])
                   - v_vel * C * (in_p_tn[idx_j0] - in_p_tn[idx_j1])
                   - w_vel * C * (in_p_tn[idx_k0] - in_p_tn[idx_k1]);

      pbndry(nx,ny,nz,in_p_tf);

      in_p_tp = in_p_tn;
      in_p_tn = in_p_tf;
   }

   in_p_rs = in_p_tf;

}

【问题讨论】:

    标签: opencl


    【解决方案1】:

    两个有用的技巧:

    1. 注释掉内核部分,在主机端查看结果看是否跑通。
    2. 在内核内部,您还可以使用if(get_global_id(0)==0) printf("test\n"); 查看它运行多远以及卡在哪里。您可能需要启用 printf,具体取决于您拥有的 GPU:#pragma OPENCL EXTENSION cl_amd_printf : enable / #pragma OPENCL EXTENSION cl_intel_printf : enable

    补充几点:

    • 内核的问题可能是其中的嵌套循环。如果内核运行的迭代次数过多/时间过长,它可能会中止。
    • 如果两个嵌套循环中的迭代相互独立,请将它们并行化。
    • 完全摆脱内核中的for(int t=1;t&lt;t_siz;t++) 时间步循环。相反,让内核只进行一次迭代,并在主机端调用内核t_siz 次。

    【讨论】:

    • 感谢您的建议。 1. 我目前使用的是 NVIDIA GPU。我找不到 NVIDIA GPU 的任何编译指示。只是在内核中使用printf() 会给我CL_​OUT_​OF_​RESOURCES 错误。在这种情况下我该怎么办? 2. 当您提到“两个嵌套循环”时,您是指内核中的void pbndry 函数吗?元素是相互独立的,所以我想我可以并行化它们。在这种情况下,是否可以使用 int i = get_local_id(0) 之类的东西? 3. 调用内核t_siz 会不会效率很低(我对OpenCL 很陌生,所以我真的不知道)?
    • 1.对于 Nvidia printf 默认启用,您不需要 pragma。 CL_​OUT_​OF_​RESOURCES 源于您的内核由于嵌套循环而运行时间过长。并行化它们。 2. 正是。一个线程应该只计算一个网格点,请参阅此处stackoverflow.com/a/61652001/9178992 使用int n = get_local_id(0); 并通过模数和整数除法将此线性索引分解为 3 个空间坐标(对于 2D:int x=n%size_x, y=n/size_x;)。 3. 如果每个内核调用 1 个时间步使 GPU 饱和(因为每个 GPU 内核计算单个网格点),则不会。
    • 在像你这样的数值 n 体/网格集成中,也不可能在单个内核中执行多个时间步长。您需要在每个时间步之后进行全局同步,而这只有在内核结束时才能实现。可以这样想:GPU 并行计算每个网格点(为此摆脱嵌套循环),其中一些比其他点更早并且以随机顺序计算。只有上一步中的所有网格点都完成后,才能开始下一个时间步。
    • 阅读您的回复后,我想我应该重新编写整个代码以适应本地大小、组大小等。我无法理解“将线性索引分解为 3 个空间坐标”。你能解释一下这是干什么用的吗?我没有在主代码中指定本地工作大小。我应该这样做吗?如果我只是在void pbndry() 中使用get_global_id() 可以吗?
    • 好的。谢谢。我会试试的。
    猜你喜欢
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    • 2015-12-15
    • 1970-01-01
    相关资源
    最近更新 更多