【问题标题】:My kernel's FLOP efficiency is very low我的内核的 FLOP 效率很低
【发布时间】:2016-05-02 23:32:07
【问题描述】:

我实现了一个用于压力梯度计算的内核。根据我使用的算法和之前的部分,ngb_list[] 是随机的,我没有合并内存访问。然而,内核的双精度 FLOP 效率是 TESLA K40 上峰值性能的 0.2%。好像很低……!

还有:

全局内存负载效率:45.05%
全局内存存储效率:100.00%

有没有办法提高 DP FLOP 效率和 Global Memory Load Efficiency?

你可以在这里找到代码:

#include <cuda.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/device_ptr.h>
#include <thrust/sequence.h>
#include <thrust/reduce.h>
#include <thrust/scan.h>
#include <thrust/execution_policy.h>
#include <iostream>
#include <time.h>
#include <cmath>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <numeric>

    typedef double  Float;

__global__ void pGrad_calculator(Float* pressure, Float* pressure_list,
                                 Float* interactionVectors_x, Float* interactionVectors_y, Float* interactionVectors_z,
                                 int* ngb_offset, int* ngb_size, int* ngb_list,
                                 Float* pressureGrad_x, Float* pressureGrad_y, Float* pressureGrad_z,
                                 int num){

    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;

    if (idx < num){

        for (int j = ngb_offset[idx]; j < (ngb_offset[idx] + ngb_size[idx]); j++){
            Float pij = (pressure[idx] + pressure[ngb_list[j]]);
            pressureGrad_x[idx] += interactionVectors_x[j] * pij; 
            pressureGrad_y[idx] += interactionVectors_y[j] * pij;
            pressureGrad_z[idx] += interactionVectors_z[j] * pij;
        }
        pressureGrad_x[idx] *= 0.5;
        pressureGrad_y[idx] *= 0.5;
        pressureGrad_z[idx] *= 0.5;
    }
}

int main(){

    const int num = 1 << 20;
    const int tb = 1024;
    int bg = (num + tb - 1) / tb;

    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);

    //ngb_size
    thrust::device_vector<int> ngb_size(num,27);

    //ngb_offset
    thrust::device_vector<int> ngb_offset(num);
    thrust::exclusive_scan(ngb_size.begin(),ngb_size.end(), ngb_offset.begin());

    //ngh list
    int ngbSize = thrust::reduce(ngb_size.begin(),ngb_size.end());

    std::cout << "ngbSize" << ngbSize << std::endl;

    thrust::device_vector<int> ngb_list(ngbSize);

    srand((unsigned)time(NULL));
    for (int i = 0; i < num; i++){

        int R = (rand()%(num - 0)) + 0;

        ngb_list[i] = R;
    }

    //pressure
    thrust::device_vector<Float> d_pressure(num);
    thrust::sequence(d_pressure.begin(),d_pressure.end(),1);

    //interaction vectors
    thrust::device_vector<Float> d_xInteractionVectors(ngbSize,1);
    thrust::device_vector<Float> d_yInteractionVectors(ngbSize,0);
    thrust::device_vector<Float> d_zInteractionVectors(ngbSize,0);

    //pressure gradients
    thrust::device_vector<Float> pGradx(num);
    thrust::device_vector<Float> pGrady(num);
    thrust::device_vector<Float> pGradz(num);

    //Pressure list
    thrust::device_vector<Float> pressure_list(ngbSize,0);

    cudaEventRecord(start);
    pGrad_calculator<<<bg,tb>>>(thrust::raw_pointer_cast(&d_pressure[0]),
                                thrust::raw_pointer_cast(&pressure_list[0]),
                                thrust::raw_pointer_cast(&d_xInteractionVectors[0]),
                                thrust::raw_pointer_cast(&d_yInteractionVectors[0]),
                                thrust::raw_pointer_cast(&d_zInteractionVectors[0]),
                                thrust::raw_pointer_cast(&ngb_offset[0]),
                                thrust::raw_pointer_cast(&ngb_size[0]),
                                thrust::raw_pointer_cast(&ngb_list[0]),
                                thrust::raw_pointer_cast(&pGradx[0]),
                                thrust::raw_pointer_cast(&pGrady[0]),
                                thrust::raw_pointer_cast(&pGradz[0]),
                                num);

    cudaEventRecord(stop);
    cudaEventSynchronize(stop);

    float milliseconds = 0;
    cudaEventElapsedTime(&milliseconds, start, stop);

    std::cout << "KERNEL TIME =  " << milliseconds << "  milliseconds" << std::endl;

    return 0;
}

【问题讨论】:

  • 从您的内核看来,它具有较低的计算复杂度和大量的全局内存加载和存储,这就是您在分析结果中看到的。此外,根据大小向量,内核可能非常发散。
  • 我认为这个问题的唯一答案是否定的 - FLOP 与内存事务的比率非常低,并且未合并负载。
  • 正如其他人所指出的,您将受到此内核中带宽的最大影响。您可以尝试将const __restrict__ 添加到interactionVectors 以查看这是否有助于提高读取性能。请参阅docs.nvidia.com/cuda/kepler-tuning-guide/… 了解更多信息。
  • @talonmies 执行循环 Unroll 将显着提高性能。你还有什么意见吗?
  • @jefflarkin 谢谢你的评论。我测试了一下,好像没有效果。

标签: performance cuda gpu gpgpu


【解决方案1】:

编译器可能无法正确优化您的代码,并且使用了许多额外的加载和写入。

尝试编写如下代码:

__global__ void pGrad_calculator(Float* pressure, Float* pressure_list,
                                 Float* interactionVectors_x, 
                                 Float* interactionVectors_y, 
                                 Float* interactionVectors_z,
                                 int* ngb_offset, int* ngb_size, 
                                 int* ngb_list, Float* pressureGrad_x, 
                                 Float* pressureGrad_y, Float* pressureGrad_z,
                                 int num)
{
    unsigned int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < num){
        Float x=pressureGrad_x[idx];
        Float y=pressureGrad_y[idx];
        Float z=pressureGrad_z[idx];
        Float pressure_local=pressure[idx];
        int offset=ngb_offset[idx];
        int end=offset+ngb_size[idx];
        for (int j = offset; j < end; j++){
            Float pij = (pressure_local + pressure[ngb_list[j]]);
            x += interactionVectors_x[j] * pij; 
            y += interactionVectors_y[j] * pij;
            z += interactionVectors_z[j] * pij;
        }
        pressureGrad_x[idx] = 0.5*x;
        pressureGrad_y[idx] = 0.5*y;
        pressureGrad_z[idx] = 0.5*z;
    }
}

但是即使进行了这种优化,由于内存带宽的限制,您也很可能无法达到峰值触发器。您每双/ 8 字节执行的浮点操作少于两个。这为您提供了 288 GB/s * 2 FOps / 8 字节 = 72 GFlop/s 或大约 5% 峰值的峰值触发器上限。

【讨论】:

  • 非常感谢您的回答。我应用了更改并进行了测试。性能稍微好一点(3% - 5%)
  • 它现在是否以峰值性能的 3-5% 运行?与您之前所说的 0.2% 性能相比,这将是一个巨大的改进? (> 15 倍加速)还是快 3-5%? (0.2% -> 0.21%) 如果它现在以 3-5% 的峰值运行,这就是您可以从该 GPU 获得的所有期望,因为内核受到内存带宽的限制。
  • 我的意思是,在应用您的评论之后,与 0.2% 相比,性能提高了 3% 到 5%。我还应用了展开循环技术,它非常非常有效。
猜你喜欢
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 2018-09-18
  • 2015-04-25
  • 1970-01-01
  • 2014-05-04
相关资源
最近更新 更多