我从下面 CUDA 中的矩阵乘法的经典示例实现中学到了知识。
也有人说,矩阵乘法器A和B从全局内存读取到共享内存wA / block_size次。我的第一个问题是如何从示例实现中计算出这个结果。
其次,当我看到共享内存的位置位于循环内时,它会在每次迭代时完全创建并重新加载。我想知道为什么它不放在循环之外,即放在内核函数的级别。如果与传统的 C/C++ 相比,数组是在循环其元素之前创建的。
我很乐意解释使用 CUDA 共享内存的矩阵乘法中 wA / block_size 的计算并解决循环放置问题:
计算 wA / block_size:
表达式 wA / block_size 表示每个线程需要访问全局内存以将矩阵 A 中的元素加载到单个块内的共享内存中的次数。这是一个细分:
wA:该变量表示矩阵A的宽度(列数)。 block_size:该变量定义线程块中的线程数。它通常是 2 的幂(例如 32、64、128),以与硬件架构保持一致。 推理:
每个线程块都作用于结果乘积矩阵 C 的子矩阵。 由于共享内存的大小有限,一次只能加载矩阵 A 的一小部分列。 block_size 确定有多少线程在子矩阵上进行协作。 每个线程需要访问 wA 元素的子集来计算其分配的点积。 通过将 wA 除以 block_size,我们估计将所有必要元素从全局内存带到块内共享内存所需的迭代次数。 示例:
假设 wA = 100(矩阵 A 有 100 列)且 block_size = 32。 每个线程需要访问矩阵 A 中的 wA / block_size = 100 / 32 = 3.125 个元素。 由于线程无法访问小数元素,因此我们将其向上舍入为 4。 这意味着每个线程块需要将矩阵 A 中的元素加载到共享内存中四次(wA / block_size)才能完成为其分配的子矩阵的计算。 共享内存声明的放置:
循环内部(常用方法):
在循环内声明共享内存是一种常见做法,原因如下: 灵活性:所需的共享内存大小可能取决于循环迭代器或其他动态因素。将其放置在循环内可以根据特定计算进行调整。 局部性:此方法确保仅在特定子矩阵计算需要时才分配共享内存。在某些情况下,块中的线程可能根本不需要共享内存。 效率:通过将共享内存分配限制在循环内,您可以避免不必要的内存使用,特别是对于大型数据集或复杂计算。 循环外(不太常见):
在极少数情况下,如果共享内存要求是固定的并且事先已知,则可以考虑在循环之外声明它。然而,这种方法有局限性: 灵活性较差:共享内存大小在整个内核执行过程中保持不变,这对于内存需求变化的场景可能并不理想。 潜在浪费:如果某些线程或块不利用共享内存,则可能会浪费资源。