低性能内核

时间:2013-08-28 08:54:14

标签: cuda gpgpu nvidia

我有一个CUDA内核,其中有许多操作和很少的分支。它看起来像

__global__
void kernel(Real *randomValues, Real mu, Real sigma)
{
    int row = blockDim.y * blockIdx.y + threadIdx.y;
    int col = blockDim.x * blockIdx.x + threadIdx.x;

    if ( row >= cnTimeSteps || col >= cnPaths ) return;

    Real alphaLevel = randomValues[row*cnPaths+col];
    Real q = 0.0;
    Real x = 0.0;

    if ( alphaLevel < p_low)
    {
        q = sqrt( -2*log( alphaLevel ) );
        x = (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
    }
    else if ( alphaLevel < p_high )
    {
        q = alphaLevel-0.5;
        Real r = q*q;
        x= (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q / (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1);
    }
    else
    {
        q = sqrt( -2*log( 1.0-alphaLevel ) );
        x = -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
    }

    randomValues[row*cnPaths+col] = sigma * x + mu;
}

其中abcd的所有值都是常量值(在设备常量内存中)

static __device__ __constant__ Real a1 = 1.73687;
static __device__ __constant__ Real a2 = 1.12321100;

等等。

在对内核进行分析后,我发现理论占用率为100%,但我得到的不超过60%。

我经历了thisthis GTC会谈,试图优化我的内核。

一方面,我有IPC报告平均1.32发出指令和0.62执行。指令序列化约为50%,但SM活动几乎为100%。另一方面,有大约38个有效扭曲,但有8个有资格执行下一个指令但是在扭曲问题效率上我得到大约70%的循环没有合格的扭曲。停顿原因被报告为“其他”,我认为这与计算logsqrt有关。

  1. 如果大多数周期没有合格的扭曲,SM活动如何能达到99.82%?
  2. 如何减少失速?
  3. 由于warp中的线程可能不会进入同一个分支,因此对常量内存的请求可能是seralized,这是真的吗?我应该将这些常量放在全局内存中(也可以使用共享内存)吗?
  4. 我是第一次使用Nsight Visual Studio,所以我试图找出所有性能分析的含义。顺便说一句,我的卡是Quadro K4000。

3 个答案:

答案 0 :(得分:3)

  

1)如果大部分周期没有,SM活动如何能达到99.82%   符合条件的经编?

如果将warp分配给warp,则warp处于活动状态。 如果SM上至少有1个warp处于活动状态,则SM处于活动状态。

SM活动不应与效率相混淆。

  

2)如何减少失速?

在上面的代码的情况下,warp停止等待双精度执行单元可用。 Quadro K4000的吞吐量为8个线程/周期,可实现双精度操作。

此问题的补救措施是: 一个。减少双精度运算的次数。例如,将连续操作移动到float可以显着提高性能,因为单精度浮点吞吐量是24倍双精度吞吐量。 湾在GK110上执行内核,GK110的灵敏度是GK10x的两倍精度。

增加占用率可能不会提高K4000上内核的性能。您提供的信息不足以确定为什么实现入住率明显低于理论占用率。

Achieved FLOPs实验可用于确认内核性能是否受双精度吞吐量的约束。

  

3)由于warp中的线程可能不会进入同一个分支,因此对常量内存的请求可能是seralized,这是真的吗?我应该将这些常量放在全局内存中(也可以使用共享内存)吗?

代码在常量内存负载中没有内存地址差异。变形控制流分歧只意味着在每个请求上一部分线程都将处于活动状态。

初始全局负载可能无法合并。您需要提供cnPaths的值才能让某人查看。您还可以查看记忆实验或源相关实验。

if和else语句可能能够以更有效的方式编码,以允许编译器使用预测而不是分歧分支。

答案 1 :(得分:1)

我假设您的Real数据类型是float的typedef。您可以尝试将f后缀添加到使用的常量值,以防止编译器添加不必要的强制转换。

E.g。

q = alphaLevel-0.5;

常量0.5是double值,alphaLevel是real = float值。 alphaLevel将被转换为double。 q是float类型。减法的结果必须再次下降到浮点数。

如果Real是dobule的typedef,那么你的所有计算都会混合double和float,导致相同的向上和向下转换。

答案 2 :(得分:0)

你可以通过简化来减少扭曲分歧的影响:

if ( alphaLevel < p_low)
{
    q = sqrt( -2*log( alphaLevel ) );
    x = (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
}
else if ( alphaLevel < p_high )
{
    q = alphaLevel-0.5;
    Real r = q*q;
    x= (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q / (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1);
}
else
{
    q = sqrt( -2*log( 1.0-alphaLevel ) );
    x = -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
}

为:

if ( alphaLevel >= p_low && alphaLevel < p_high )
{
    q = alphaLevel-0.5;
    Real r = q*q;
    x= (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q / (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1);
}
else
{
    alphaLevel = alphaLevel >= p_low ? 1.0-alphaLevel : alphaLevel;
    q = sqrt( -2*log( alphaLevel ) );
    x = -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
}
相关问题