-1

我正在开发一个程序,我在其中调用一个输入随机二进制数的函数。总数将在运行时提供,例如:1000 或 10,00,000.. 在生成随机数后,我需要使用 COUNTERS 计算 0 的总数和 1 的总数。我有以下查询:

  1. 我应该分配多少线程、块和网格?
  2. 我需要 2D 螺纹,还是只能使用 1D 螺纹?
  3. 什么函数线程会在里面做什么,我觉得它应该检查特定值是 1 还是 0 这听起来对吗?
  4. 我应该如何使用经纱或平铺方法?
4

1 回答 1

2

我猜这可能是一个家庭作业问题,尤其是基于您在 SO 上发布的唯一其他问题。

  1. 有多少线程/块/网格?这个问题的答案取决于你的线程策略。每个线程会做什么?对于产生大量输出的问题,例如图像处理或矩阵乘法,常见的线程策略是分配每个线程来完成创建一个输出点的工作。但是这个问题只产生少量的输出值(似乎是 2 个),并且属于包括归约、流压缩和直方图在内的一类问题。这些问题通常分两步解决(可能是 2 个内核......),一个常见的线程策略(至少对于第一步或内核)是为每个输入点分配一个线程。但另请参阅我对下面 2 的回答。一旦知道需要多少线程,通常会为每个块选择一些线程数,例如 256 或 512(绝对使用 2 的幂),
  2. 二维还是一维?您的问题本质上不是 2D,因此 1D 线程网格是一个合理的起点。但是,在一维线程网格中,您可以在网格中创建的最大线程数限制为您正在使用的 GPU 的最大网格 X 维度乘以每个块的线程数。这些数字通常类似于 65535 和 1024,因此在输入点的大约 64M 元素之后,您将用完线程。此时转换为使用 2D 网格结构并不难,这会将可能的线程数增加到大于 GPU 一次可以处理的大小。然而,另一种策略而不是切换到 2D 线程块网格是保留 1D 线程块网格,但让每个线程处理多个输入点/元素,可能在内核代码中使用循环。例如,如果您的循环最多可以处理 512 个元素,那么 65535x1024x512 应该涵盖您的问题大小。对于此类问题,这也是一种方便的线程策略,因为线程可以保留它创建的中间结果的本地副本(到目前为止的 1 和 0 的计数),而不会干扰或与其他线程同步。
  3. 我基于上述的建议是单个线程将执行一个循环,并且循环的每一次循环都会查看一个元素,并更新包含 1 和 0 计数的局部变量。这将是两部分算法的第一部分。然后,第二部分必须收集这些中间结果。您将需要考虑第二部分如何收集第一部分的结果。例如,在内核完成时,您可能希望将中间结果存储回全局内存。
  4. 翘曲/平铺?Warp是指将线程分组为32个线程的单元以供执行。这将自动为您发生。您应该安排您的算法,以便当您从全局内存中读取值(或将值写入全局内存)时,每个线程在连续的连续块中读取(或写入)。也就是说,线程 0 从位置 0 读取,线程 1 从下一个位置读取,等等。如果您在线程中没有做任何不寻常的事情,这或多或少会自动为您发生。由 cudaMalloc 创建的数据存储将正确对齐,如果您的数组索引策略类似于 [thread_number] 那么您将在整个 warp 中对齐和合并访问,建议您从 GPU 中获得良好的速度。平铺是指组织数据访问以突出局部性的过程,这通常有利于依赖缓存的架构。如果您在内存合并方面做得很好,您将不会过多地依赖缓存。

如果您有空的话,CUDA C 编程指南是一个可读性很强的文档,它将向您展示良好的 GPU 编程所需的基本概念。nvidia 网站上也有网络研讨会,可以在大约 2 小时内涵盖此处的重要材料。此外,thrust可以通过最少的编码工作(在 C++ 中)方便地处理此类问题,但我猜这超出了您现在尝试做的范围。

于 2012-09-26T03:20:08.727 回答