如何计算哪个SM给定线程正在运行?

时间:2013-02-10 21:57:21

标签: cuda nvidia

我是CUDA初学者。

到目前为止,我了解到,每个SM都有8个块(线程)。假设我有简单的工作将数组中的元素乘以2.但是,我的数据少于线程。

不是问题,因为我可以切断线程的“尾部”以使它们闲置。但是,如果我理解正确,这将意味着一些SM将获得100%的工作,而某些部分(甚至没有)。

所以我想计算哪个SM在给定线程中运行并以这种方式进行计算,每个SM都有相同的工作量。

我希望它首先有意义:-)如果是这样,如何计算给定线程运行哪个SM?或者 - 当前SM的索引和它们的总数?换句话说,在SM术语中等效于threadDim / threadIdx。

更新

评论太久了。

罗伯特,谢谢你的回答。当我尝试消化所有时,这就是我所做的 - 我有一个“大”数组,我只需要将值*2相乘并将其存储到输出数组(作为预热;顺便说一句。所有计算我做,数学上是正确的)。所以首先我在1个块,1个线程中运行它。精细。接下来,我尝试以这样的方式拆分工作,即每个乘法只由一个线程完成一次。结果我的程序运行大约6次。我甚至感觉到为什么 - 获取有关GPU的信息,然后计算我应该使用多少块和线程的小惩罚,然后在每个线程而不是单个乘法现在我有大约10个额外的乘法只是为了计算数组中的偏移量一个线程。一方面,我试图找出如何改变这种不受欢迎的行为,另一方面我想在SM中均匀地传播线程的“尾部”。

我改写 - 也许我错了,但我想解决这个问题。我有1G小工作(*2就是全部) - 我应该用1K线程创建1K块,或者用1个线程创建1M块,用1M线程创建1块,依此类推。到目前为止,我读取了GPU属性,除法,除法,并盲目地使用网格/块的每个维度的最大值(如果没有要计算的数据,则使用所需的值)。

代码

size是输入和输出数组的大小。一般来说:

output_array[i] = input_array[i]*2;

计算我需要多少块/线程。

size_t total_threads = props.maxThreadsPerMultiProcessor
                       * props.multiProcessorCount;
if (size<total_threads)
    total_threads = size;

size_t total_blocks = 1+(total_threads-1)/props.maxThreadsPerBlock;

size_t threads_per_block = 1+(total_threads-1)/total_blocks;  

props.maxGridSizeprops.maxThreadsDim我以类似的方式计算块和线程的维度 - 来自total_blocksthreads_per_block

然后是杀手部分,计算线程的偏移量(“线程内部”):

size_t offset = threadIdx.z;
size_t dim = blockDim.x;
offset += threadIdx.y*dim;
dim *= blockDim.y;
offset += threadIdx.z*dim;
dim *= blockDim.z;
offset += blockIdx.x*dim;
dim *= gridDim.x;
offset += blockIdx.y*dim;
dim *= gridDim.y;

size_t chunk = 1+(size-1)/dim;

所以现在我有当前线程的起始偏移量,以及用于乘法的数组(块)中的数据量。我没有使用上面的grimDim.z,因为AFAIK总是1,对吧?

1 个答案:

答案 0 :(得分:6)

这是一件不寻常的事情。鉴于你是一名CUDA初学者,在我看来这样的问题表明试图不正当地解决问题。你想解决的问题是什么?如果您在SM X与SM Y上执行特定线程,它如何帮助您解决问题?如果你想要从机器中获得最大的性能,那么以一种所有线程处理器和SM都可以处于活动状态的方式构建你的工作,并且实际上有足够多的工作&#34;对全部。 GPU依赖于超额订阅资源来隐藏延迟。

作为CUDA初学者,您的目标应该是:

  • 在块和线程中创建足够的工作
  • 有效地访问内存(这主要与合并有关 - 你可以阅读它)

确保&#34;每个SM都有相同的工作量并没有任何好处。如果在网格中创建了足够的块,则每个SM 具有大致相等的工作量。这是调度程序的工作,您应该让调度程序执行此操作。如果你没有创建足够的块,你的第一个目标应该是创建或找到更多的工作要做,而不是为每个块提出一个不会产生任何好处的花哨的工作细分。

Fermi GPU中的每个SM(例如)都有32个线程处理器。为了使这些处理器即使在由于存储器访问等而存在不可避免的机器停顿的情况下也保持忙碌,该机器被设计为通过在停止时交换另一个线程的扭曲(32)来隐藏延迟发生,以便处理可以继续。为了实现这一点,您应该尝试每个SM拥有大量可用的warp。这有助于:

  • 网格中的许多线程块(至少是GPU中SM数量的6倍)
  • 每个threadblock有多个warp(可能至少有4到8个warp,所以每个块有128到256个线程)

由于(Fermi)SM总是一次执行32个线程,如果我的GPU中的线程数少于任何时刻的GPU数量的32倍,那么我的机器利用率不高。如果我的整个问题仅由20个线程组成,那么它根本没有很好地设计来利用任何GPU,并且将这20个线程分解为多个SM /线程块不太可能有任何明显的好处

编辑:由于您不想发布您的代码,我还会提出更多建议或意见。

  1. 你试图修改一些代码,发现它运行得慢,然后跳到(我认为)错误的结论。
  2. 您应该熟悉一个简单的代码示例,例如vector add。它没有使每个元素相乘,但结构很接近。使用单个线程执行此向量添加无法实际运行得更快。我想如果你研究这个例子,你会找到一种直接的方法来扩展它来做数组元素乘以2。
  3. 没有人按照你概述的方式计算每个块的线程数。首先,每块的线程数应该是32的倍数。其次,习惯上每个块选择线程作为起点,并从中构建其他启动参数,而不是相反。对于一个大问题,只需从每个块的256或512个线程开始,并省去计算。
  4. 根据您选择的线程块大小构建其他启动参数(网格大小)。您的问题本质上是1D,因此1D网格块的1D网格是一个很好的起点。如果此计算超出了x维中最大块的机器限制,那么您可以让每个线程循环处理多个元素,或者扩展到2D网格(1D线程块)。
  5. 您的偏移计算不必要地复杂。请参阅向量添加示例,了解如何使用相对简单的偏移计算来创建线程网格以处理数组。