我需要有关优化内核和设备代码的建议。据我所知,CUDA文档(以及许多幻灯片)建议使用大线程块大小来隐藏内存和算术延迟。
我的内核和设备功能非常耗费计算能力。因此,我尝试使用尽可能多的寄存器(显然),因为这样我在占用方面妥协。对于我的应用程序来说,指令级并行性比大型线程块更重要。
但ILP背后的基本思想是拥有独立的指令。我的问题是
1)如何实现这一目标?在计算中,总有变量可以重复用于其他计算
2)任何人都可以建议或提供一些例子,其中依赖指令可以转换成独立的指令吗?
3)我还读(在某处)对于算术运算,可以实现最大ILP = 4,即线程计算4个独立指令。这是否意味着,如果存在这样的四条指令,并且在此之后存在相关指令,则warp将进入等待,直到满足依赖性为止?
4)任何人都可以提出一些利用ILP的阅读材料和代码吗?
我在这里也提供了一些分析代码;它可能没意思。代码表示以下等式:
关键是我想要达到最佳性能;我想用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
答案 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)的结果来利用交换i
和j
不会改变denom_prod
这一事实
d)reduction * reduction
比pow(reduction, 2)
关于你的问题:
1)和2)看到我的评论a)。
3)这可能是指费米生成GPU(计算能力2.x)每个SM有两个独立的warp调度程序,每个循环能够在每个周期发出两个指令,每个周期总共最多四个指令
然而,依赖指令的问题远不止于此,因为依赖指令遭受~16..24个周期的等待时间。即两个相关指令中的第二个必须在发出之前等待那么多个周期。其间的循环可以由来自相同warp的独立指令使用(必须位于相关指令之间,因为当前的Nvidia GPU不能无序地发出指令)。或者它们可以被其他经线的指令使用,这些经线总是独立的。因此,为了获得最佳性能,您需要许多warp或连续的独立指令,或者理想情况下两者都需要。4)瓦西里·沃尔科夫的出版物对这一主题进行了精彩的阅读,特别是他的"Better Performance at Lower Occupancy"演讲。