CUDA独立指令优化

时间:2013-05-24 17:55:34

标签: optimization cuda

我需要有关优化内核和设备代码的建议。据我所知,CUDA文档(以及许多幻灯片)建议使用大线程块大小来隐藏内存和算术延迟。

我的内核和设备功能非常耗费计算能力。因此,我尝试使用尽可能多的寄存器(显然),因为这样我在占用方面妥协。对于我的应用程序来说,指令级并行性比大型线程块更重要。

但ILP背后的基本思想是拥有独立的指令。我的问题是

1)如何实现这一目标?在计算中,总有变量可以重复用于其他计算 2)任何人都可以建议或提供一些例子,其中依赖指令可以转换成独立的指令吗? 3)我还读(在某处)对于算术运算,可以实现最大ILP = 4,即线程计算4个独立指令。这是否意味着,如果存在这样的四条指令,并且在此之后存在相关指令,则warp将进入等待,直到满足依赖性为止?
4)任何人都可以提出一些利用ILP的阅读材料和代码吗?

我在这里也提供了一些分析代码;它可能没意思。代码表示以下等式:

Formula

关键是我想要达到最佳性能;我想用ILP。我的代码中还有其他设备功能;所以我正在使用

螺纹块:192
14 SM(32芯):448(芯)
每个SM同时使用8个块:8 x 192:1536
“ - ptxas-options = -v”编译代码时,每个线程有50个寄存器(占用率约为33%)

等式中使用的所有参数都是double型(除n之外) 例如n = 2. params数组在param [0]中包含S,在param [1]中包含I1,在param [2]中包含I2

#define N 3.175e-3
__device__ double gpu_f_different_mean(double x, double params[], int n) {

   double S = params[0];
   double product_I = 1.0;

   for (int i = 1; i <= n; i++) {
      product_I = product_I * params[i];
   }

   double tmp   = S * exp(-N * S * x);
   double outer = product_I * tmp;

   double result = 0.0;

   for (int i = 1; i <=n; i++) {

      double reduction = (params[i] + S * x);
      double numerator = 1 + N * reduction;

      double denom_prod = 1.0;
      for (int j = 1; j<= n; j++) {
         if ( i != j)
            denom_prod = denom_prod * (params[j] - params[i]);
      }

      double denominator = pow(reduction, 2) * denom_prod;
      result             = result + (numerator / denominator);
   }

   return outer * result;
}

硬件

我正在使用Fermi Architecture GPU GTX470,计算能力2.0

1 个答案:

答案 0 :(得分:4)

几条评论:

a)通过引入多个约简变量可以打破由denom_prod的连续更新引起的依赖关系链:

  double denom_prod1 = 1.0;
  double denom_prod2 = 1.0;
  int j;
  for (j = 1; j <= n-1; j += 2) {
     if ( i != j)
        denom_prod1 *= (params[j  ] - params[i]);
     if ( i != j+1)
        denom_prod2 *= (params[j+1] - params[i]);
  }
  if (j < n) {
     if ( i != j)
        denom_prod1 = denom_prod * (params[j  ] - params[i]);
  }
  double denom_prod = denom_prod1 * denom_prod2;

b)循环中的条件可以通过将循环分为两部分来消除:

  double denom_prod = 1.0;
  for (int j = 1; j < i; j++)
     denom_prod = denom_prod * (params[j] - params[i]);
  for (int j = i+1; j <= n; j++)
     denom_prod = denom_prod * (params[j] - params[i]);

c)您可以通过一次计算(i,j)和(j,i)的结果来利用交换ij不会改变denom_prod这一事实

d)reduction * reductionpow(reduction, 2)

更快(可能更准确)

关于你的问题:

1)和2)看到我的评论a)。

3)这可能是指费米生成GPU(计算能力2.x)每个SM有两个独立的warp调度程序,每个循环能够在每个周期发出两个指令,每个周期总共最多四个指令

然而,依赖指令的问题远不止于此,因为依赖指令遭受~16..24个周期的等待时间。即两个相关指令中的第二个必须在发出之前等待那么多个周期。其间的循环可以由来自相同warp的独立指令使用(必须位于相关指令之间,因为当前的Nvidia GPU不能无序地发出指令)。或者它们可以被其他经线的指令使用,这些经线总是独立的。因此,为了获得最佳性能,您需要许多warp或连续的独立指令,或者理想情况下两者都需要。

4)瓦西里·沃尔科夫的出版物对这一主题进行了精彩的阅读,特别是他的"Better Performance at Lower Occupancy"演讲。