当前位置: 首页 > 知识库问答 >
问题:

映射GPU内存时是否应使用volatile?

东郭凯捷
2023-03-14

OpenGL和Vulkan都允许通过分别使用glMapBuffervkMapMemory来获取指向部分GPU内存的指针。它们都为映射的内存提供了val*。要将其内容解释为一些数据,必须将其转换为适当的类型。最简单的例子可以是转换为浮点数*以将内存解释为浮点数或向量或类似数组

似乎任何类型的内存映射在C中都是未定义的行为,因为它没有内存映射的概念。但是,这并不是真正的问题,因为这个主题超出了C标准的范围。但是,仍然存在易失性的问题。

在链接问题中,指针被额外标记为volatile,因为它所指向的内存的内容可以以编译器在编译期间无法预料的方式进行修改。虽然我很少看到人们在这种情况下使用volatile(更广泛地说,这个关键字现在似乎很少使用),但这似乎是合理的。

与此同时,在这个问题中,答案似乎是没有必要使用易失性。这是因为他们所说的内存是使用mmap映射的,后来又提供给msync,这可以被视为修改内存,这类似于在Vulkan或OpenGL中显式刷新它。恐怕这既不适用于OpenGL也不适用于Vulkan。

如果内存被映射为不是GL_MAP_FLUSH_EXPLICIT_BIT或它是VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,则根本不需要刷新,内存内容会自动更新。即使使用vkFlushMep dMemoryRangesglushMep dBufferRange手动刷新内存,这些函数实际上都不会将映射的指针作为参数,因此编译器不可能知道它们修改了映射的内存内容。

因此,是否有必要将指向映射GPU内存的指针标记为volatile?我知道从技术上讲,这都是未定义的行为,但我想问的是,在实际硬件上需要什么。

顺便说一下,Vulkan规范和OpenGL规范都没有提到volatile限定符。

编辑:将内存标记为易失性会产生性能开销吗?

共有1个答案

韦嘉颖
2023-03-14

好吧,假设我们有一个编译器,它对代码中发生的一切都是全知的。这意味着编译器可以跟踪任何指针,即使每次代码的运行时执行都完全正确,无论您如何隐藏它。因此,即使您在程序的一端读取一个字节,编译器也会以某种方式记住您所读取的确切字节,并且每当您再次尝试读取它们时,它可以选择不执行该读取,而只给您以前的值,除非编译器知道可以更改它的内容。

但我们也可以说,我们无所不知的编译器完全无视OpenGL/Vulkan中发生的一切。对于这个编译器来说,图形API是一个黑盒。这里有龙。

因此,您从API获取一个指针,从中读取,GPU向其写入,然后您想读取GPU刚刚写入的新数据。为什么编译器会认为该指针后面的数据已被更改;毕竟,这些改动来自系统之外,来自C标准不认可的来源。

这就是易失性的用途,对吧?

好吧,事情是这样的。在OpenGL和Vulkan中,为了确保您能够真正读取这些数据,您需要做一些事情。即使一致地映射内存,也必须进行API调用,以确保写入内存的GPU进程已实际执行。对于Vulkan来说,你在等待一个篱笆或一个事件。对于OpenGL,您需要等待一段时间或执行一次完整的完成。

无论哪种方式,在从内存执行读取之前,全能编译器都会遇到一个函数调用到一个黑盒子中,而正如前面建立的那样,编译器对这个黑盒子一无所知。由于映射指针本身来自同一个黑盒,编译器不能假定黑盒没有指向该内存的指针。就编译器而言,调用这些函数可能会将数据写入该内存。

因此,我们无所不知但不经意的编译器无法优化这种内存访问。一旦我们从这些函数获得控制权,编译器必须假设通过该地址可访问的任何指针的任何内存都可能被更改。

如果编译器能够窥视图形API本身,阅读并理解这些html" target="_blank">函数正在做什么,那么它肯定会看到一些东西,告诉它,“哦,我不应该对通过这些指针检索到的内存状态做出假设。”

这就是为什么您不需要volatile。

此外,请注意,这同样适用于写入数据。如果您写入持久、连贯的映射内存,您仍然必须与图形API执行一些同步操作,以便您的CPU写入以使GPU不会读取它。因此,编译器知道它不能再依赖于它对以前写入的数据的了解。

 类似资料:
  • 问题内容: 我用np.save()保存了几个numpy数组,并将它们放在一起非常大。 是否可以将它们全部加载为内存映射文件,然后对它们进行串联和切片,而无需将任何内容都加载到内存中? 问题答案: 使用显然将数组加载到内存中。为避免这种情况,您可以轻松地在新文件中创建一个thrid数组,并从要连接的数组中读取值。以更有效的方式,您还可以将新阵列追加到磁盘上已存在的文件中。 在任何情况下,您都必须为数

  • 系统调用在调用进程的虚拟地址空间中提供映射,将文件或设备映射到内存中。 下面是两种类型 - 文件映射或文件支持的映射 - 此映射将进程的虚拟内存区域映射到文件。 这意味着读取或写入这些内存区域会导致文件被读取或写入。这是默认的映射类型。 匿名映射 - 此映射映射进程的虚拟内存区域,不受任何文件的支持。 内容被初始化为零。 这种映射类似于动态内存分配(malloc()),在某些实现中用于某些分配。

  • 我正在考虑在GPU上处理数据的可能性,这对于GPU内存来说太大了,我有几个问题。 在CUDA 6.0中,UM不允许超额订阅GPU内存(并且通常不允许分配比GPU拥有的内存更多的内存,即使在主内存中也是如此),但是在CUDA 8.0中,这是可能的(https://devblogs.nvidia.com/parallelforall/beyond-gpu-memory-limits-unified-m

  • 问题内容: 我想检测特定页面是否已在内存中映射。目的是能够在使用固定内存地址调用mmap之前执行此检查。以下代码说明了默认情况下的情况:mmap默默地重新映射原始内存页面。 我知道我可以打开并解析/ proc / self / maps来确定已分配的内存范围,并从中推断出以下内容:如果我可以使用mmap安全地请求特定的内存范围,但是我正在寻找合适的API:一个东西 ? 问题答案: msync(ad

  • 读 # mmap_read.py import mmap with open('lorem.txt', 'r') as f: with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m: print('First 10 bytes via read :', m.read(10