CUDA中与SM/core相关的内存分配和索引
我有以下设置,顶级父内核称为:CUDA中与SM/core相关的内存分配和索引,cuda,Cuda,我有以下设置,顶级父内核称为: parent_kernel<<<a, 1>>>(...) parent_内核(…) 它所做的唯一事情就是调用一系列子内核: child_kernel_1<<<c1, b1>>>(... + offset(blockIdx.x), blockIdx.x) child_kernel_2<<<c2, b2>>>(... + offset(blockIdx.x)
parent_kernel<<<a, 1>>>(...)
parent_内核(…)
它所做的唯一事情就是调用一系列子内核:
child_kernel_1<<<c1, b1>>>(... + offset(blockIdx.x), blockIdx.x)
child_kernel_2<<<c2, b2>>>(... + offset(blockIdx.x), blockIdx.x)
...
child_kernel_1(…+偏移量(blockIdx.x),blockIdx.x)
子线程内核线程2(…+偏移量(blockIdx.x),blockIdx.x)
...
我需要将child\u kernel\u 1
的结果传递给child\u kernel\u 2
。
说中间结果比任何输入都大,所以我不能重用它们的内存(至少是直接重用)。此外,当在a
上组合时,它们足够大,无法装入GPU内存,这意味着在父内核
之前进行批量预分配不是一个选项
这给我留下了malloc
在parent\u内核中,这也不是一件好事,因为内存分配非常耗时,a
可能相当大
同时,同一时间只执行有限数量的块,并且在a
中将所有分配的块设置为相同大小不会增加太多开销
这让我想知道,是否有可能将索引绑定到SM/core(或类似),而不是块?(当另一个块在同一个SM/core下调用时,上一个必须已经完成,并且可以安全地重用内存) 让我们以一种可能的方式来讨论已经讨论过的问题
总体概念如下
基于父内核(…)
,我们有a
子内核启动序列要执行。a
中的每个项目由子内核1
和子内核2
组成。有大量临时数据需要从子1传递到子2,我们不希望预先分配所有a
数量的此类临时数据
我们观察到,对于GPU中的每个SM,可能存在最大数量的驻留块X
;这是运行时可查询的CUDA硬件限制(例如,deviceQuery
sample code)
假设我们的GPU中有W
条短信(在运行时也可以查询)。让我们假设对于每个SM,驻留块上的硬件限制是X
。这意味着我们应该只需要提供W*X
临时分配,如果W*X
小于a
,我们可能有办法通过减少临时分配规模来解决此问题。(为了正确执行此步骤,X
可能需要根据所讨论内核的占用率分析进行缩减。)
为了使用这一途径,我们需要限制我们启动的区块总数,以便每个SM只有X
,即我们必须启动W*X
区块。由于这小于a
(推测),我们必须从以下方面重新构建父内核设计:
child_kernel_1<<<c1, b1>>>(... + offset(blockIdx.x), blockIdx.x)
child_kernel_2<<<c2, b2>>>(... + offset(blockIdx.x), blockIdx.x)
在第一种方法中,我提到启动W*X
块,但要正确使用该方法,必须进行占用率分析。由于占用率分析,可能需要减少数量W*X
。代码示例中指出的第二种方法可以对启动的任意数量的块正确工作。当然可以通过SM/block管理分配,请参阅。@RobertCrovella它忽略了我所指的粒度级别(核心),这很重要,因为SM太过强制,线程可以在锁步中执行,从而导致不必要的内存访问模式。不,根本没有办法将任何内容绑定到CUDA内核。CUDA核心大约是一个浮点ALU。无法保证它将用于什么用途,或者它将涉及哪些线程或指令。假设每个块有1个线程,那么就不可能在lockstep中执行线程。如果您了解最大可能占用率,您应该能够管理每个SM的分配,正如链接的答案所描述的。也许您不了解CUDA核心是什么。最接近CPU核心的是CUDA SM。CUDA SM不是由多个CUDA内核组成的,就像CPU由多个内核组成一样。@RobertCrovella对于链接的答案,您的意思是通过特殊寄存器确定SM,然后通过原子锁管理对与该特定SM相关联的内存块的访问?
for (int i = 0; i < a/(W*X); i++){
child_kernel_1<<<c1, b1>>>(i, ... + offset(blockIdx.x), blockIdx.x)
child_kernel_2<<<c2, b2>>>(i, ... + offset(blockIdx.x), blockIdx.x)}
#include <iostream>
#include <cassert>
const long long DELAY_T = 100000;
// this is used to get one of a set of unique slots on the SM
//const unsigned long long slots = 0xFFFFFFFFULL; // 0xFFFFFFFF assumes 32 unique slots per SM
const int max_num_slots = 32;
const unsigned long long busy = 0x1FFFFFFFFULL;
__device__ int get_slot(unsigned long long *sm_slots){
unsigned long long my_slots;
bool done = false;
int my_slot;
while (!done){
while ((my_slots=atomicExch(sm_slots, busy)) == busy); // wait until we get an available slot
my_slot = __ffsll(~my_slots) - 1;
if (my_slot < max_num_slots) done = true;
else atomicExch(sm_slots, my_slots);} // handle case where all slots busy, should not happen
unsigned long long my_slot_bit = 1ULL<<my_slot;
unsigned long long retval = my_slots|my_slot_bit;
assert(atomicExch(sm_slots, retval) == busy);
return my_slot;
}
__device__ void release_slot(unsigned long long *sm_slots, int slot){
unsigned long long my_slots;
while ((my_slots=atomicExch(sm_slots, busy)) == busy); // wait until slot access not busy
unsigned long long my_slot_bit = 1ULL<<slot;
unsigned long long retval = my_slots^my_slot_bit;
assert(atomicExch(sm_slots, retval) == busy);
}
__device__ int __mysmid(){
int smid;
asm volatile("mov.u32 %0, %%smid;" : "=r"(smid));
return smid;}
__global__ void k(unsigned long long *sm_slots, int *temp_data){
int my_sm = __mysmid();
int my_slot = get_slot(sm_slots+my_sm);
temp_data[my_sm*max_num_slots + my_slot] = blockIdx.x;
long long start = clock64();
while (clock64()<start+DELAY_T);
assert(temp_data[my_sm*max_num_slots + my_slot] == blockIdx.x);
release_slot(sm_slots+my_sm, my_slot);
}
int main(){
// hard coding constants for Tesla V100 for demonstration purposes.
// these should instead be queried at runtime to match your GPU
const int num_sms = 80;
const int blocks_per_sm = 32;
// slots must match the number of blocks per SM, constants at top may need to be modified
assert(blocks_per_sm <= max_num_slots);
unsigned long long *d_sm_slots;
int *d_data;
cudaMalloc(&d_sm_slots, num_sms*blocks_per_sm*sizeof(unsigned long long));
cudaMalloc(&d_data, num_sms*blocks_per_sm*sizeof(int));
cudaMemset(d_sm_slots, 0, num_sms*blocks_per_sm*sizeof(unsigned long long));
k<<<123456, 1>>>(d_sm_slots, d_data);
cudaDeviceSynchronize();
if (cudaGetLastError()!=cudaSuccess) {std::cout << "failure" << std::endl; return 0;}
std::cout << "success" << std::endl;
return 0;
}