我的GTX680真的很棒吗?

时间:2012-10-29 11:31:20

标签: cuda

我正在尝试测试我的GTX680的计算性能,因为我有点怀疑它的真实性能。我想知道是否有人也可以测试他的GTX 680,如果给出相同的结果,或者告诉我可以做些什么来更好地从卡中获得更多性能

我写过这个小程序

#include <stdlib.h>
#include <stdio.h>
#include <float.h>
#include "cuComplex.h"
#include "time.h"
#include "cuda_runtime.h"
#include <iostream>
using namespace std;
__global__ void test(int loop, int *out)
{
    register int a=0;
    for (int x=0;x<loop;x++)
    {
        a+=x*loop;
    }


    if (out!=NULL) *out=a;


}
int main(int argc, char *argv[])
{
    float timestamp;
    cudaEvent_t event_start,event_stop;
    // Initialise
    cudaDeviceReset();
    cudaDeviceReset();
    cudaSetDevice(0);
    cudaThreadSetCacheConfig(cudaFuncCachePreferShared);        
    // Allocate and generate buffers
    cudaEventCreate(&event_start);
    cudaEventCreate(&event_stop);
    cudaEventRecord(event_start, 0);
    dim3 threadsPerBlock;
    dim3 blocks;
    int b=1000; 
    threadsPerBlock.x=32;
    threadsPerBlock.y=32;
    threadsPerBlock.z=1;
    blocks.x=1;
    blocks.y=1000;
    blocks.z=1;

    test<<<blocks,threadsPerBlock,0>>>(300,
            NULL
            );

    cudaEventRecord(event_stop, 0);
    cudaEventSynchronize(event_stop);
    cudaEventElapsedTime(&timestamp, event_start, event_stop);
    printf("Calculated in %f", timestamp);
}

使用nvcc进行编译我得到了这个PTX

//
// Generated by NVIDIA NVVM Compiler
// Compiler built on Sat Sep 22 02:35:14 2012 (1348274114)
// Cuda compilation tools, release 5.0, V0.2.1221
//

.version 3.1
.target sm_30
.address_size 64

    .file   1 "/tmp/tmpxft_00000e7b_00000000-9_perf.cpp3.i"
    .file   2 "/opt/home/daniel/a/perf.cu"

 .visible .entry _Z4testiPi(
    .param .u32 _Z4testiPi_param_0,
    .param .u64 _Z4testiPi_param_1
 )
 {
    .reg .pred      %p<4>;
    .reg .s32       %r<15>;
    .reg .s64       %rd<3>;


    ld.param.u32    %r6, [_Z4testiPi_param_0];
    ld.param.u64    %rd2, [_Z4testiPi_param_1];
    cvta.to.global.u64      %rd1, %rd2;
    mov.u32         %r13, 0;
    .loc 2 12 1
    setp.lt.s32     %p1, %r6, 1;
    mov.u32         %r14, %r13;
    mov.u32         %r11, %r13;
    @%p1 bra        BB0_2;

 BB0_1:
    .loc 2 14 1
    mad.lo.s32      %r14, %r11, %r6, %r14;
    .loc 2 12 20
    add.s32         %r11, %r11, 1;
    .loc 2 12 1
    setp.lt.s32     %p2, %r11, %r6;
    mov.u32         %r13, %r14;
    @%p2 bra        BB0_1;

 BB0_2:
    .loc 2 18 1
    setp.eq.s64     %p3, %rd2, 0;
    @%p3 bra        BB0_4;

    .loc 2 18 1
    st.global.u32   [%rd1], %r13;

 BB0_4:
    .loc 2 21 2
    ret; 
 }

内核运行时间为1.936ms

我的计算表明,GFLOPS的性能是1.1 TFLOP,仅为3TFLOPS理论值的三分之一(参考:http://www.geforce.com/hardware/desktop-gpus/geforce-gtx-680)。为什么它这么慢?

我的计算详情如下

mad.lo.s32      %r14, %r11, %r6, %r14;  //2 FLOPS
.loc 2 12 20
 add.s32         %r11, %r11, 1;     //1 FLOP
.loc 2 12 1
 setp.lt.s32     %p2, %r11, %r6;    //1 FLOP
 mov.u32         %r13, %r14;        // 1 FLOP
 @%p2 bra        BB0_1;             //1 FLOP

 + 1 FLOP (just as a buffer as I don't know branching how much it takes)

循环中1次迭代的总FLOPS为7 FLOPS

仅考虑迭代

我们每个线程有300次迭代 我们有1024 * 1000块

总迭代次数FLOPS = 300 * 1024 * 1000 * 7 = 2.15 GFLOPS

总内核时间是1.936ms

因此吞吐量= 1.11 TFLOPS

提前感谢您的帮助

丹尼尔

4 个答案:

答案 0 :(得分:3)

此示例程序以@Robert Crovella答案为基础。 Robert的内核受数据依赖性的限制。通过减少FMA指令之间的数据依赖性,这个内核应该在GTX680上实现2.4-2.5 TFLOPS。

当前实现是指令获取和数据依赖性受限。应该能够调整内核以将已实现的FLOPS提高10%。

Nsight Visual Studio Edition 2.x和新的3.0 RC候选版提供了分析此内核所需的指标。

在2.x和3.0中,您应该使用以下实验来分析内核:

  1. 指令统计 - SM活动 - 验证所有SM是否接近100%
  2. 问题效率 - 符合条件的扭曲 - 开普勒符合条件的扭曲每个活动周期必须大于4,以便每个扭曲调度程序每个周期发出一条指令。
  3. 问题效率 - 问题停顿 - 扭曲问题效率将指定每个扭曲调度程序由于符合条件的warp数量不足而无法发出的频率。如果这个很高,那么问题停滞原因将有助于识别限制器。
  4. 实现FLOP - 显示内核执行的单精度和双精度浮点运算的类型和速率的细分。
  5. 在Robert的内核的情况下,执行依赖性非常高,因为每个指令都具有读写后依赖性。通过增加指令级并行性,我们将性能提高了三倍。内核现在主要是取指令限制。

    新的Nsight VSE 3.0 RC(今天可用)还将显示使用每条指令统计信息注释的汇编或源代码,例如执行的指令数和每条指令的活动线程数。在此示例中,该工具可用于识别数据依赖性,并确保编译器生成FMA指令,这些指令需要达到理论上实现的FLOPS的50%以上。

    __global__ void test(float loop, float *out)
    {
        register float a=1.0f;
        register float b=1.0f;
        register float c=1.0f;
        register float d=1.0f;
        register float e=1.0f;
        register float f=1.0f;
        register float g=1.0f;
        register float h=1.0f;
    
        for (float x=0;x<loop;x++)
        {
            a+=x*loop;
            b+=x*loop;
            c+=x*loop;
            d+=x*loop;
            e+=x*loop;
            f+=x*loop;
            g+=x*loop;
            h+=x*loop;
    
            a+=x*loop;
            b+=x*loop;
            c+=x*loop;
            d+=x*loop;
            e+=x*loop;
            f+=x*loop;
            g+=x*loop;
            h+=x*loop;
    
            a+=x*loop;
            b+=x*loop;
            c+=x*loop;
            d+=x*loop;
            e+=x*loop;
            f+=x*loop;
            g+=x*loop;
            h+=x*loop;
    
            a+=x*loop;
            b+=x*loop;
            c+=x*loop;
            d+=x*loop;
            e+=x*loop;
            f+=x*loop;
            g+=x*loop;
            h+=x*loop;
    
            a+=x*loop;
            b+=x*loop;
            c+=x*loop;
            d+=x*loop;
            e+=x*loop;
            f+=x*loop;
            g+=x*loop;
            h+=x*loop;
        }
        if (out!=NULL) *out=a+b+c+d+e+f+g+h;
    }
    
    int main(int argc, char *argv[])
    {
        float timestamp;
        cudaEvent_t event_start,event_stop;
        // Initialise
        cudaDeviceReset();
        cudaSetDevice(0);
        cudaThreadSetCacheConfig(cudaFuncCachePreferShared);
        // Allocate and generate buffers
        cudaEventCreate(&event_start);
        cudaEventCreate(&event_stop);
        cudaEventRecord(event_start, 0);
        dim3 threadsPerBlock;
        dim3 blocks;
        threadsPerBlock.x=32;
        threadsPerBlock.y=32;
        threadsPerBlock.z=1;
        blocks.x=1;
        blocks.y=1000;
        blocks.z=1;
    
        test<<<blocks,threadsPerBlock,0>>>(30,NULL);
    
        cudaEventRecord(event_stop, 0);
        cudaEventSynchronize(event_stop);
        cudaEventElapsedTime(&timestamp, event_start, event_stop);
        printf("Calculated in %f\n", timestamp);
    }
    

答案 1 :(得分:1)

我认为问题在于你使用的是整数乘法。计算能力3.0体系结构上的32位整数乘法仅为32位浮点吞吐量的1/6(​​参见下表,取自CUDA C编程指南5.5版)。将32位整数乘法性能与3.0体系结构的32位浮点性能进行比较。

主要在计算应用程序中使用的一些其他整数操作和类型转换在3.0上的性能也同样降低。

enter image description here

答案 2 :(得分:0)

使用此代码查看您是否获得了更好的结果。这只是一个例子,它与你的代码没有完全相同的东西,我认为你将不得不重新计算翻牌。

#include <stdio.h>
using namespace std;
__global__ void test(float loop, float *out)
{
    register float a=1.0f;
    for (float x=0;x<loop;x++)
    {
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
        a+=x*loop;
    }


    if (out!=NULL) *out=a;


}
int main(int argc, char *argv[])
{
    float timestamp;
    cudaEvent_t event_start,event_stop;
    // Initialise
    cudaDeviceReset();
    cudaSetDevice(0);
    cudaThreadSetCacheConfig(cudaFuncCachePreferShared);
    // Allocate and generate buffers
    cudaEventCreate(&event_start);
    cudaEventCreate(&event_stop);
    cudaEventRecord(event_start, 0);
    dim3 threadsPerBlock;
    dim3 blocks;
    threadsPerBlock.x=32;
    threadsPerBlock.y=32;
    threadsPerBlock.z=1;
    blocks.x=1;
    blocks.y=1000;
    blocks.z=1;

    test<<<blocks,threadsPerBlock,0>>>(30,
            NULL
            );

    cudaEventRecord(event_stop, 0);
    cudaEventSynchronize(event_stop);
    cudaEventElapsedTime(&timestamp, event_start, event_stop);
    printf("Calculated in %f\n", timestamp);
}

当我用arch = sm_20或sm_30编译它时,我在内核循环中连续获得10个fma指令,没有中间代码。我认为它会比你的代码运行得更快,更接近峰值理论翻转。是的,整数OP /秒和浮点OP /秒之间存在差异。如果你确实运行了这段代码,请回复一下,让我知道你计算的性能是多少。

答案 3 :(得分:0)

您的测试内核正在执行整数运算,而不是浮点运算。因此,FLOPS是该内核的错误指标。

FLOPS = FLoating point Operations Per Second

回到最初的问题,你的内核很慢,因为GPU针对浮点计算进行了优化,而不是整数计算。

要进行正确的测试,请尝试将测试内核转换为使用浮点数而不是整数。

此外,在您将FLOPS注释到步骤的循环中,FLOPS再次没有意义,因为它是每秒度量并且这些是整数运算。转换后,只需将它们计为单独的浮点运算,而不是每秒浮点运算。