【问题标题】:OpenCL When to use global, private, local, constant address spacesOpenCL 何时使用全局、私有、本地、常量地址空间
【发布时间】:2018-01-07 15:14:49
【问题描述】:

我正在尝试学习 OpenCL,但我很难决定使用哪些地址空间,因为我只找到了声明这些地址空间是什么的组合资源,但没有说明它们存在的原因或何时使用它们。资源至少太分散了,所以带着这个问题,我希望把所有这些信息都收集起来:所有的地址空间是什么,它们为什么存在,什么时候使用哪个地址空间,关于内存的优缺点是什么和性能。

据我了解(这可能过于简化),GPU 有两种物理类型的内存:全局内存,与实际的处理器相距甚远,速度慢但相当大,可供所有工作人员使用和本地内存,接近实际的处理器,速度如此之快,但很小,其他工作人员无法访问。

直观地说,local 限定符确保将变量放置在本地内存中,global 限定符确保将变量放置在全局内存中,尽管我不确定这正是发生的情况。这留下了 privateconstant 限定符。这些的目的是什么?

还有一些隐含的限定符。例如,the specifications 提到了通用地址空间,我认为它用于没有限定符的参数。这到底是做什么的?然后还有局部函数变量。这些地址空间是什么?

这是一个使用我的直觉但不知道我实际在做什么的例子:

示例: 假设我将一个类型为 long 且长度为 10000 的数组传递给一个仅用于读取的内核,然后我将其声明为 global const,因为它必须可供所有工作人员使用并且它不会改变。为什么我不使用constant 限定符?当通过 CPU 为这个数组设置缓冲区时,我实际上也可以将数组设为只读,在我看来,这与声明它 const 相同。再说一次,我何时以及为什么要声明 constantglobal const

在执行内存密集型任务时,将数组复制到内核内部的本地数组会更好吗?我的猜测是本地内​​存太小了,但是如果数组只有 10 的长度呢?数组什么时候会太大/太小?更笼统地说:什么时候值得将数据从全局内存复制到本地内存?

假设我还想传递这个数组的长度,那么我会将const int length 添加到我的内核的参数中,但我不确定为什么我会省略global 限定符,除非因为我见过其他人做。毕竟,length 必须可供所有工作人员访问。如果我是对的,那么length 将有一个通用地址空间,但同样,我真的不知道这意味着什么。

我希望有经验的人能解决这个问题。这不仅对我很有帮助,我也希望对其他想要获得有关 GPU 内存管理的实用知识的爱好者也有帮助。

【问题讨论】:

  • 常量:从所有内核访问同一个单元的速度很快。全局:对合并的邻居地址的访问速度很快。 local:没有冲突的访问速度很快。私人:它的快。例外:单个全局/本地可以广播到所有核心。 global 服务于 gpu 的所有内核,local 服务于一个计算单元的所有内核,constant 服务于 gpu 的所有内核,private 服务于单个内核。核心,我的意思是工作项。

标签: memory opencl memory-address


【解决方案1】:

常量:一小部分缓存的全局内存对所有工作人员可见。如果可以,请使用它,只读。

全局:速度慢,所有人都能看到,读或写。它是您所有数据的终点,因此始终需要对其进行一些访问。

本地:您需要在本地群组中分享内容吗?使用本地!您的所有本地工作人员是否都访问相同的全局内存?使用本地! 本地内存仅在本地工作人员内部可见,并且大小有限,但速度非常快。

私有:只有工作人员才能看到的内存,将其视为寄存器。默认情况下,所有未定义的值都是私有的。


假设我将一个长度为 10000 的类型的数组传递给一个内核,我 只会用于阅读,然后我将它声明为 global const ,因为它必须 可供所有工人使用,并且不会改变。为什么我不使用 常量限定符?

实际上,是的,您可以并且应该使用constant 限定符。它将您的数据放在常量内存上(所有工作人员可以快速访问的一小部分只读内存)。 GPU 使用它来将制服传输到所有顶点着色器。

当通过 CPU 为这个数组设置缓冲区时,我其实也 只是可以使数组成为只读的,在我看来,这就是 与将其声明为 const 相同。再说一次,我什么时候以及为什么要宣布 常量或全局常量?

并非如此,当您创建一个只读缓冲区时,您只是指定 OpenCL 您计划以只读方式使用它,因此它可以在后面进行优化,但您实际上可以从内核写入它。 global const 只是对开发者的一个保障,所以你不要不小心写到它,它会在编译时报错。 基本上,与普通 C 主机端计算相同。如果所有内存都是非常量的,程序也可以正常工作。

在执行内存密集型任务时,将数组复制到内核内部的本地数组会更好吗?我的猜测是本地内​​存太小了,但是如果数组只有 10 的长度呢?数组什么时候会太大/太小?更笼统地说:什么时候值得将数据从全局内存复制到本地内存?

只有在所有工人都阅读时才有价值。如果每个worker读取全局内存的单个值,那么它是不值得的。 在这里有用:

Worker0 -> Reads 0,1,2,3
Worker1 -> Reads 0,1,2,3
Worker2 -> Reads 0,1,2,3
Worker3 -> Reads 0,1,2,3

这里没用:

Worker0 -> Reads 0
Worker1 -> Reads 1
Worker2 -> Reads 2
Worker3 -> Reads 3

假设我也想传递这个数组的长度,那么我会添加 const int length 到我的内核的参数,但我不确定为什么我 会省略全局限定符,除非因为我见过其他 人们这样做。毕竟,所有工作人员都必须能够访问长度。如果 我是对的,那么 length 将有一个通用地址空间,但同样, 我真的不知道那是什么意思。

当您未在内核参数中指定限定符时,它通常默认为 constant,这是您希望这些小元素能够被所有工作人员快速访问的。

OpenCL 编译器通常遵循的内核参数规则是:如果它只读取并适合常量,则为常量,否则为全局。

【讨论】:

  • 谢谢,这澄清了一些事情。只是一些问题:如果我理解正确,通用地址空间中的所有变量实际上只会被编译器分配另一个地址空间?那么局部函数变量呢?
  • 函数在 OpenCL 中并不是真正的函数。它们总是由编译器内联,因此它们占用传递给它们的变量正在使用的地址空间。在极少数情况下,您在需要地址空间(原子、屏障等)的函数中使用方法,您应该在函数中定义它,因此如果函数被滥用,编译器会给出错误。
  • 有多少“恒定”内存可用?我们在说kb,mb吗?有没有办法知道有多少可用?
猜你喜欢
  • 2013-02-04
  • 2015-02-17
  • 2021-09-14
  • 1970-01-01
  • 1970-01-01
  • 2012-03-06
  • 2019-06-01
  • 2020-11-14
  • 1970-01-01
相关资源
最近更新 更多