【问题标题】:Global Memory vs. Dynamic Global Memory Allocation in CUDACUDA 中的全局内存与动态全局内存分配
【发布时间】:2025-12-13 01:20:04
【问题描述】:

我有一个需要使用全局内存的 CUDA (v5.5) 应用程序。理想情况下,我更喜欢使用常量内存,但我已经用尽了常量内存,溢出将不得不放在全局内存中。我也有一些变量需要偶尔写入(在 GPU 上进行一些缩减操作之后),我将其放置在全局内存中。

为了阅读,我将以一种简单的方式访问全局内存。我的内核在 for 循环中被调用,每次调用内核时,每个线程都将访问完全相同的全局内存地址,没有偏移。对于写入,在每次内核调用之后在 GPU 上执行缩减,并且我必须在循环的下一次迭代之前将结果写入全局内存。然而,在我的应用程序中,读取的次数远多于写入全局内存的次数。

我的问题是,与使用动态分配的全局内存相比,使用在全局(变量)范围内声明的全局内存是否有任何优势?我需要的全局内存量将根据应用程序而变化,因此动态分配会更可取。但是,我知道全局内存使用的上限,而且我更关心性能,因此我也可以使用我确信不会溢出的大型固定分配静态声明内存。考虑到性能,有什么理由更喜欢一种形式的全局内存分配而不是另一种?它们是否存在于 GPU 上的同一个物理位置并且它们的缓存方式是否相同,或者两种形式的读取成本是否不同?

【问题讨论】:

    标签: c++ cuda


    【解决方案1】:

    Global memory 可以分配statically(使用__device__)、dynamically(使用设备mallocnew)和通过CUDA runtime(例如使用cudaMalloc)。

    上述所有方法在物理上分配相同类型的内存,即从板载(但不是片上)DRAM 子系统中切出的内存。无论分配方式如何,此内存都具有相同的访问、合并和缓存规则(因此具有相同的一般性能注意事项)。

    由于动态分配需要一些非零时间,因此可以通过在程序开始时使用静态(即__device__)方法或通过运行时进行一次分配来提高代码的性能API(即cudaMalloc等)这避免了在代码的性能敏感区域花时间动态分配内存。

    另请注意,我概述的 3 种方法虽然具有类似 C/C++ 的设备代码访问方法,但具有不同的主机访问方法。静态分配的内存使用cudaMemcpyToSymbolcudaMemcpyFromSymbol等运行时API函数访问,运行时API分配的内存通过普通的cudaMalloc/cudaMemcpy类型函数访问,动态分配的全局内存(设备new和@987654336 @) 不能直接从主机访问。

    【讨论】:

    • 谢谢,这正好回答了我的问题,还有一些我什至没有问的问题。
    【解决方案2】:

    首先,您需要考虑合并内存访问。你没有提到你正在使用的 GPU。在最新的 GPU 中,煤系内存读取将提供与恒定内存相同的性能。所以尽可能让你的记忆以煤炭的方式读写。

    另一个你可以使用纹理内存(如果数据大小适合它)。这个纹理内存有一些缓存机制。这以前在全局内存读取未合并的情况下使用。但最新的 GPU 在纹理和全局内存方面提供几乎相同的性能。

    我认为全局声明的内存不会比动态分配的全局内存提供更多性能,因为合并问题仍然存在。在 CUDA 全局内存的情况下,在全局(变量)范围内声明的全局内存也是不可能的。可以全局(在程序中)声明的变量是常量内存变量和纹理,我们不需要将它们作为参数传递给内核。

    有关内存优化,请参阅 cuda c 最佳实践指南http://docs.nvidia.com/cuda/cuda-c-best-practices-guide/#memory-optimizations 中的内存优化部分

    【讨论】:

    • 感谢您的指点。我在亚马逊 AWS 上使用 Tesla M2050s。我理解合并问题。但是,我猜从静态和动态分配的全局内存的合并读取之间是否有任何区别是问题的症结所在。另外,我不确定最后一行说“唯一全局声明的变量是常量内存变量和纹理”。据此(developer.nvidia.com/content/…),CUDA 全局内存中存在全局(变量)范围。这个链接并没有解决我原来的问题。
    • 对于内存优化,请参阅cuda c最佳实践指南docs.nvidia.com/cuda/cuda-c-best-practices-guide/…中的内存优化部分@