内核块执行之间GPU多处理器的共享内存会发生什么变化?

时间:2013-12-13 23:45:27

标签: cuda scheduling

假设我有一堆带有一堆块的CUDA内核,并假设在同一个对称多处理器上的另一个块之后安排了一个块(即,所有warp的共享内存区域相同的单元)。目前,NVIDIA没有在API或每GPU文档中指定执行之间共享内存会发生什么。但实际上,关于块的共享内存内容,下列哪一项呢? :

  • 它处于同一状态,最后一个预定的阻止它。
  • 这是空白的。
  • 它包含不可预见的垃圾。

为了缩小可能出现的情况的变化,请具体参考每个块使用最大可能共享内存量的情况 - 在Kepler GPU上为48 KB。

2 个答案:

答案 0 :(得分:6)

NVIDIA不会在此级别发布硬件行为,因此您应该将其视为未定义(如@datenwolf所说)。当然,虽然给定块看到的共享内存的内容不是随机的。并且硬件没有必要花时间清理内存。

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

API无法阻止块检测它正在运行的位置。块的调度是动态确定的,可能很难预测。

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

修改

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

在GTX660(Kepler,CC 3.0,5个SM)上配置了48K共享内存,CC 3.0 Release版本,我得到了以下结果:

C:\rd\projects\cpp\test_cuda\Release>test_cuda.exe 10000
Shared memory initializations: 5

我跑了好几次,每次都得到相同的结果。这与我最初的猜测相匹配,因为10000个整数占用~40K,因此每个SM可以有一个并发块的空间,并且该设备有5个SM。

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

Shared memory initializations: 32,822
Shared memory initializations: 99,996
Shared memory initializations: 35,281
Shared memory initializations: 30,748

因此,在这种情况下,我对固定位置的猜测完全无效。

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

Shared memory initializations: 480

所以,再次,不是预期的数字,并且令人惊讶的是,即使每个块使用的共享内存量较小,也可能存在较少的可能变化。

看起来,如果你决定用脚射击自己,你可以使用一个大的共享内存块来保持一致:)此外,这是在一个也用于显示的GPU上运行,Windows 7 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;
    }
  }
}

答案 1 :(得分:1)

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