映射 GPU 内存时应该使用 volatile 吗?

如何解决映射 GPU 内存时应该使用 volatile 吗?

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

在 C++ 中似乎任何类型的内存映射 is undefined behaviour,因为它没有内存映射的概念。但是,这并不是真正的问题,因为该主题超出了 C++ 标准的范围。但是,仍然存在volatile的问题。

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

同时在 this question 中,答案似乎是使用 volatile 是不必要的。这是因为他们所说的内存是使用 mmap 映射的,然后给了 msync,它可以被视为修改内存,这类似于在 Vulkan 或 OpenGL 中显式刷新它。恐怕这不适用于 OpenGL 和 Vulkan。

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

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

顺便说一下,Vulkan SpecificationOpenGL Specification 都没有提到 volatile 限定词。

编辑:将内存标记为 volatile 会导致性能开销吗?

解决方法

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

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

所以你从 API 得到一个指针,从中读取,GPU 写入它,然后你想要读取 GPU 刚刚写入的新数据。为什么编译器会认为该指针后面的数据已被更改?毕竟,更改来自系统外部,来自 C++ 标准无法识别的来源。

这就是 volatile 的用途,对吗?

好吧,事情就是这样。在 OpenGL 和 Vulkan 中,为了确保您可以真正读取该数据,您需要做一些事情。即使你连贯地映射内存,你也必须进行 API 调用,以确保写入内存的 GPU 进程已经实际执行。对于 Vulkan,您正在等待围栏或事件。对于 OpenGL,您正在等待或执行完整的操作。

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

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

如果编译器能够查看图形 API 本身,以阅读和理解这些函数在做什么,那么它肯定会看到一些会告诉它的东西,“哦,我不应该对通过这些指针检索的内存。”

这就是您不需要 volatile 的原因。

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-