Cuda GPU多处理器';内核块执行之间的共享内存?

Cuda GPU多处理器';内核块执行之间的共享内存?,cuda,scheduling,Cuda,Scheduling,假设我有一个CUDA内核,它有一堆块,并且假设在同一个对称多处理器上,一个块被排在另一个块的后面(也就是说,所有扭曲都有相同的共享内存区域的单元)。NVIDIA目前没有在API或每个GPU文档中指定执行之间共享内存的变化。但实际上,关于块的共享内存内容,以下哪项适用 它的状态与上一个调度块离开它时的状态相同 它是空白的 它包含不可预见的垃圾 为了缩小可能出现的情况的变化范围,请特别参考每个块在开普勒GPU上使用最大可能共享内存量(48 KB)的情况。状态未定义。这意味着它可以是任何东西,包括

假设我有一个CUDA内核,它有一堆块,并且假设在同一个对称多处理器上,一个块被排在另一个块的后面(也就是说,所有扭曲都有相同的共享内存区域的单元)。NVIDIA目前没有在API或每个GPU文档中指定执行之间共享内存的变化。但实际上,关于块的共享内存内容,以下哪项适用

  • 它的状态与上一个调度块离开它时的状态相同
  • 它是空白的
  • 它包含不可预见的垃圾

为了缩小可能出现的情况的变化范围,请特别参考每个块在开普勒GPU上使用最大可能共享内存量(48 KB)的情况。

状态未定义。这意味着它可以是任何东西,包括你猜到的三件事中的任何一件。但是从未初始化的内存中读取可能会导致你的GPU也出现一个人工智能。

NVIDIA并没有在这个级别上发布硬件的行为,所以你应该把它看作是未定义的(如@ DeNeW狼)。当然,给定块看到的共享内存的内容不会是随机的。硬件没有必要花时间清除内存

GPU可以在每个SM上同时运行多个块。给定内核同时运行的块数取决于各种因素。因此,例如,如果共享内存是限制因素,那么每个SM将运行尽可能多的块,以适应共享内存。因此,如果有48K的共享内存,而一个块需要10K,那么可以同时运行4个块,使用40K。因此,如果你有一个有8条短信的设备,我猜给定块的共享内存将有32(4*8)个可能的固定位置。因此,当一个新的块被调度时,它将被分配到其中一个位置,并看到共享内存,因为它是在该位置运行的前一个块留下的

API没有为块提供检测其运行位置的方法。块的调度是动态确定的,可能很难预测

如果GPU用于显示,它可能同时运行其他内核(着色器),可能以奇怪而奇妙的方式覆盖CUDA内核中块之间的共享内存。甚至CUDA也可能在幕后运行其他内核

编辑:

我写了一个小程序来测试(包括在下面)。程序将一个块应存储在共享内存中的整数数作为参数。然后它启动100000个块,每个块有一个线程。每个块检查其共享内存是否已初始化。如果已初始化,则块不再执行任何操作。如果未初始化,则该块初始化内存并增加全局计数。初始化模式是一个递增的数字序列,以避免初始化共享内存缓冲区出现部分重叠

在GTX660(开普勒,CC 3.0,5 SMs),配置了48K共享内存,CC 3.0发行版上,我得到了以下结果:

C:\rd\projects\cpp\test\u cuda\Release>test\u cuda.exe 10000
共享内存初始化:5
我运行了几次,每次都得到相同的结果。这与我最初的猜测相符,因为10000个整数占用了~40K的空间,所以每个SM可以容纳一个并发块,而这个设备有5条SMs

然而,当我将共享内存减少到2500个整数(~10K),期望得到20次初始化,并多次运行时,我得到了不同的高数值:

共享内存初始化:32822
共享内存初始化:99996
共享内存初始化:35281
共享内存初始化:30748
因此,我对固定位置的猜测在这种情况下是完全无效的

然后,我尝试将共享内存减少到100个整数(48K中可以容纳122个块),并始终得到:

共享内存初始化:480
因此,同样不是预期的数字,而且令人惊讶的是,尽管每个块使用的共享内存量较小,但可能的变化明显较少

看起来,如果你决定开枪打自己的脚,你可以使用一个大的共享内存块来保持一致:)而且,这是在一个GPU上运行的,该GPU也用于显示,Windows7和Aero(一个GPU加速主题)而且渲染似乎不会产生干扰,因为在内核运行时桌面会冻结

节目:

#include "cuda_runtime.h"

#include <iostream>
#include <sstream>
using namespace std;

#define assertCudaSuccess(ans) { _assertCudaSuccess((ans), __FILE__, __LINE__); }
inline void _assertCudaSuccess(cudaError_t code, char *file, int line)
{
  if (code != cudaSuccess) {
    fprintf(stderr,"CUDA Error: %s %s %d\n", cudaGetErrorString(code), file, line);
    exit(code);
  }
}

__global__ void shared_memory_persistence_test(int n_shared_ints);
__device__ int init_cnt_d(0);

int main(int argc, char* argv[])
{
  cout.imbue(locale(""));
  int n_shared_ints;
  stringstream(string(argv[1])) >> n_shared_ints;
  shared_memory_persistence_test<<<dim3(100, 1000), 1, n_shared_ints * sizeof(int)>>>(n_shared_ints);
  assertCudaSuccess(cudaPeekAtLastError());
  assertCudaSuccess(cudaDeviceSynchronize());
  int init_cnt_h;
  assertCudaSuccess(cudaMemcpyFromSymbol(&init_cnt_h, init_cnt_d, sizeof(int), 0, cudaMemcpyDeviceToHost));
  cout << "Shared memory initializations: " << init_cnt_h << endl;
  return 0;
}

__global__ void shared_memory_persistence_test(int n_shared_ints)
{
  extern __shared__ int shared[];

  for (int i(0); i < n_shared_ints; ++i) {
    if (shared[i] != i) {
      for (int i(0); i < n_shared_ints; ++i) {
        shared[i] = i;
      }
      atomicAdd(&init_cnt_d, 1);
      break;
    }
  }
}
#包括“cuda_runtime.h”
#包括
#包括
使用名称空间std;
#定义assertCudaSuccess(ans){assertCudaSuccess((ans),_文件_uuu,_行_uu);}
内联void\u assertCudaSuccess(cudaError\u t代码,char*文件,int行)
{
如果(代码!=cudaSuccess){
fprintf(标准,“CUDA错误:%s%s%d\n”,cudaGetErrorString(代码)、文件、行);
出口(代码);
}
}
__全局无效共享内存持久性测试(int n共享int);
__设备初始化(0);
int main(int argc,char*argv[])
{
cout.imbue(区域设置(“”);
int n_共享int;
stringstream(string(argv[1]))>>n\u共享\u int;
共享内存持久性测试(n个共享整数);
assertCudaSuccess(cudaPeekAtLastError());
assertCudaSuccess(cudaDeviceSynchronize());
int init_cnt_h;
assertCudaSuccess(cudaMemcpyFromSymbol(&init_cnt_h,init_cnt_d,sizeof(int),0,cudaMemcpyDeviceToHost));

cout@einpoklum:这个问题不可能回答,因为行为可能会随着每个驱动程序版本的变化而变化,甚至可能取决于GPU所使用的系统硬件配置。请某人给你一个未定义行为的定义确实让我感到非常奇怪。感谢这些愿意我很难回答