Cuda 计算双FMA和共享内存延迟的精确方法

Cuda 计算双FMA和共享内存延迟的精确方法,cuda,latency,Cuda,Latency,我试图找到一种准确的方法来测量两个操作的延迟: 1) 双精度FMA操作的延迟。 2) 从共享内存加载双精度的延迟。 我使用的是K20x,我想知道这段代码是否能给出准确的测量结果 #include <cuda.h> #include <stdlib.h> #include <stdio.h> #include <iostream> using namespace std; //Clock rate #define MHZ 732e6 //num

我试图找到一种准确的方法来测量两个操作的延迟: 1) 双精度FMA操作的延迟。 2) 从共享内存加载双精度的延迟。 我使用的是K20x,我想知道这段代码是否能给出准确的测量结果

#include <cuda.h>

#include <stdlib.h>
#include <stdio.h>
#include <iostream>

using namespace std;

//Clock rate
#define MHZ 732e6
//number of streaming multiprocessors
#define SMS 14
// number of double precision units
#define DP_UNITS 16*4
//number of shared banks
#define SHARED_BANKS 32

#define ITER 100000
#define NEARONE 1.0000000000000004

__global__ void fma_latency_kernal(double *in, double *out){
  int tid = blockIdx.x*blockDim.x+threadIdx.x;
  double val = in[tid];
#pragma unroll 100
  for(int i=0; i<ITER; i++){
    val+=val*NEARONE;
  }
  out[tid]=val;
}

__global__ void shared_latency_kernel(double *in, double *out){
  volatile extern __shared__ double smem[];
  int tid = blockIdx.x*blockDim.x+threadIdx.x;
  smem[threadIdx.x]=in[tid];
#pragma unroll 32
  for(int i=0; i<ITER; i++){
    smem[threadIdx.x]=smem[(threadIdx.x+i)%32]*NEARONE;
  }
  out[tid]=smem[threadIdx.x];
}

int main (int argc , char **argv){

  float time;
  cudaEvent_t start, stop, start2, stop2;

  double *d_A, *d_B;
  cudaMalloc(&d_A, DP_UNITS*SMS*sizeof(float));
  cudaMalloc(&d_B, DP_UNITS*SMS*sizeof(float));

  cudaError_t err;

  cudaEventCreate(&start);
  cudaEventCreate(&stop);
  cudaEventRecord(start, 0);

  fma_latency_kernal<<<SMS, DP_UNITS>>>(d_A, d_B);

  cudaEventRecord(stop, 0);
  cudaEventSynchronize(stop);
  cudaEventElapsedTime(&time, start, stop);
  time/=1000;
  err = cudaGetLastError();
  if(err!=cudaSuccess)
    printf("Error FMA: %s\n", cudaGetErrorString(err));
  printf("Latency of FMA = %3.1f clock cycles\n", (time/(double)ITER)*(double)MHZ);


  cudaDeviceSetSharedMemConfig(cudaSharedMemBankSizeFourByte);
  cudaEventCreate(&start2);
  cudaEventCreate(&stop2);
  cudaEventRecord(start2, 0);

  shared_latency_kernel<<<1, SHARED_BANKS, sizeof(double)>>>(d_A, d_B );

  cudaEventRecord(stop2, 0);
  cudaEventSynchronize(stop2);
  cudaEventElapsedTime(&time, start2, stop2);
  time/=1000;
  err = cudaGetLastError();
  if(err!=cudaSuccess)
    printf("Error Shared Memory: %s\n", cudaGetErrorString(err));

  printf("Latency of Shared Memory = %3.1f clock cycles\n", time/(double)ITER*(double)MHZ);

}
#包括
#包括
#包括
#包括
使用名称空间std;
//时钟频率
#定义MHZ 732e6
//流式多处理器的数量
#定义SMS 14
//双精度单位的数量
#定义DP_单元16*4
//共享银行的数量
#定义共享存储库32
#定义ITER 100000
#定义NEARONE 1.0000000000000004
__全局\uuuuu无效fma\u延迟\u内核(双入双出){
int tid=blockIdx.x*blockDim.x+threadIdx.x;
双瓦尔=英寸[tid];
#布拉格展开100

对于(inti=0;i您的延迟值在我看来非常高-几乎是我预期的两倍。要测量GPU上的某个周期,可以插入clock()函数在内核函数的相关部分之前和之后执行。时钟函数以int形式返回当前周期,因此通过从第二个值中减去第一个值,可以得到调度第一条时钟指令和调度第二条时钟指令之间经过的周期数


请注意,此方法得到的数字将包括时钟指令本身的额外时间;我认为默认情况下,线程将在每个时钟指令之前和之后立即阻塞几个周期,因此您可能需要对此进行实验,以查看它添加了多少周期,以便您可以将它们减去。

r结果似乎是大致的,但有点高。您可能需要稍微改进您的方法。基于我的性能优化工作,我建议将SMs超额订阅约20倍,也就是说,运行的线程数是可以同时运行的线程数的20倍。这减少了GPU中各种开销的影响,表现出稳定的性能-状态性能。您可能对以前的微基准测试研究感兴趣:,虽然您当前的代码似乎不会受到影响,但这里有一个小警告:GPU上的指令缓存大小很小,我认为在4KB到8KB的范围内。指令很大(通常包含8个字节)。没有分支预测。这意味着,如果展开的循环太大,无法完全放入指令缓存,那么当它们遇到循环关闭分支时,将遇到强制的ICache未命中。根据我的实验,这可能会导致大约3%的性能损失(这显然因代码上下文而异,可能因GPU体系结构而异)。谢谢提醒。我将尝试使用展开。我不确定在超额订阅SM时如何测量延迟。如果我开始向SM发送许多扭曲,它们将开始重叠执行指令。在这种情况下,您如何消除延迟?或者您是否建议我设置共享内存以限制执行一次上一个扭曲?