解释 Metal 和 SIMD 中的不同类型
Posted
技术标签:
【中文标题】解释 Metal 和 SIMD 中的不同类型【英文标题】:Explaining the different types in Metal and SIMD 【发布时间】:2019-01-18 07:18:27 【问题描述】:在使用 Metal 时,我发现类型的数量令人眼花缭乱,而且我并不总是清楚应该在哪种情况下使用哪种类型。
在 Apple 的 Metal Shading Language Specification 中,有一个非常清晰的表格,说明 Metal 着色器文件中支持哪些类型。但是,有大量可用的示例代码似乎使用了 SIMD 中的其他类型。在 macOS(Objective-C)方面,Metal 类型不可用,但 SIMD 类型可用,我不确定应该使用哪些。
例如:
在 Metal Spec 中,float2
被描述为代表两个浮动组件的“矢量”数据类型。
在应用程序方面,以下所有内容似乎都以某种身份被使用或表示:
float2
,即vector_types.h中的typedef ::simd_float2 float2
注意:“在 C 或 Objective-C 中,此类型可用作 simd_float2。”
vector_float2
,即typedef simd_float2 vector_float2
注意:“此类型已弃用;您应该改用 simd_float2 或 simd::float2”
simd_float2
,即typedef __attribute__((__ext_vector_type__(2))) float simd_float2
::simd_float2
和 simd::float2
?
矩阵类型也存在类似情况:
matrix_float4x4
、simd_float4x4
、::simd_float4x4
和float4x4
、
有人能解释一下为什么有这么多看似重叠功能的 typedef 吗?如果您今天(2018 年)在 Objective-C / Objective-C++ 中编写一个新应用程序,您应该使用哪种类型来表示两个浮点值(x/y)以及可以在应用程序代码和 Metal 之间共享的矩阵变换的类型?
【问题讨论】:
【参考方案1】:带有vector_
和matrix_
前缀的类型已被弃用,取而代之的是带有simd_
前缀的类型,因此一般指导(以float4
为例)将是:
simd_float4
类型。 (除非您提供自己的 typedef
,否则您必须包含前缀,因为 C 没有命名空间。)
Objective-C 也是如此。
在 C++ 代码中,使用 simd::float4
类型,您可以通过 using namespace simd;
将其缩短为 float4
。
Objective-C++ 也是如此。
在 Metal 代码中,使用 float4
类型,因为 float4
是 Metal 着色语言 [1] 中的基本类型。
在 Swift 代码中,使用 float4
类型,因为 simd_
类型的类型别名为较短的名称。
更新:在 Swift 5 中,float4
和相关类型已被弃用,取而代之的是 SIMD4<Float>
和相关类型。
这些类型在本质上都是等效的,并且都具有相同的大小和对齐特性,因此您可以跨语言使用它们。那其实也是simd框架的设计目标之一。
因为你没有问,我将把关于打包类型的讨论留到另一天。
[1] Metal 是一个不寻常的情况,因为它在全局命名空间中定义了float4
,然后将其导入到metal
命名空间,该命名空间也作为simd
命名空间导出。它还将float4
别名为vector_float4
。因此,您可以为此向量类型使用上述任何名称(simd_float4
除外)。首选float4
。
【讨论】:
我确实没有提及打包类型,以免压倒最初的问题。我的理解是打包类型最适合用于顶点缓冲区,以帮助减少整体内存需求。 (即:加载具有许多顶点的大型对象时。)但我没有进一步探索它们。不确定是打开一个新问题还是附加到这个问题更好。 (哦,感谢 MBE!社区的一个很好的资源。) 每次都必须输入SIMD4<Float>
而不是float4
感觉像是在倒退……
嗨@warrenm。这很有帮助,但我仍然感到困惑。通常,在为 Metal 编码时,有一个顶点结构布局的共享标头,并且您的 .metal 文件和非 Metal 源都包含该标头。当您的非 Metal 源代码是纯 C 时,您必须使用 simd_float4
,但 simd_float4
是一种专门在 Metal 中不工作的变体,正如您在上面提到的。一个人该怎么办??我最终在我的共享标头中使用了float4
,并在包含该标头之前在C文件中执行typedef simd_float4 float4
,但这感觉就像一个丑陋的黑客......??
真正奇怪的是,几天来我在共享标题中使用simd_float4
,它运行良好,Metal 没有任何抱怨。然后突然之间,我没有接触过的代码产生了编译错误,因为 Metal 不再喜欢 simd_float4
。我不知道为什么;我没有对看起来完全相关的代码进行任何更改,我正在努力连接笔尖!
抱歉,请注意上面我的意思是“纯 Objective-C”(与 ObjC++ 相对),而不是“纯 C”。但困惑依然存在。【参考方案2】:
你应该使用哪种类型来表示两个浮点值 (x/y)
如果可以避免,不要如果您使用 CPU SIMD,则不要使用单个 SIMD 向量来表示单个几何 x,y
向量。
当您在每个 SIMD 向量中有许多 相同 的东西时,CPU SIMD 效果最好,因为它们实际上存储在 16 字节或 32 字节的向量寄存器中,其中两个之间的“垂直”操作向量很便宜(打包加法或乘法),但“水平”操作大多只能通过 shuffle + 垂直操作来完成。
例如,一个包含 4 个 x
值的向量和另一个包含 4 个 y
值的向量让您可以并行执行 4 个点积或 4 个叉积,而无需改组,因此整体吞吐量显着增加了点积每个时钟周期比如果你有一个[x1, y1, x2, y2]
的向量。
请参阅https://***.com/tags/sse/info,尤其是这些幻灯片:SIMD at Insomniac Games (GDC 2015),了解有关规划数据布局和程序设计的更多信息,以便并行执行许多类似操作,而不是尝试加速单个操作。
此规则的一个例外是,如果您仅通过加/减来平移坐标,因为即使使用结构数组,这仍然是纯粹的垂直操作。因此对于基于 16 字节向量的 CPU 短向量 SIMD 来说很好。 (例如,一个向量中的第二个元素只与另一个向量中的第二个元素交互,因此不需要改组。)
GPU SIMD 不同,我认为交错数据没有问题。我不是 GPU 专家。
(我不使用Objective C或Metal,所以我无法帮助您详细说明它们的类型名称,只是底层CPU硬件擅长的。x86 SSE / AVX,ARM基本相同NEON / AArch64 SIMD 或 PowerPC Altivec。水平操作较慢。)
【讨论】:
感谢您的意见和额外的见解。很高兴了解到数据是由 CPU 还是由 GPU 处理的,需要考虑不同的因素。 Apple 通常在其 Metal 示例代码中使用 float2 或 float3 来表示 2-D 或 3-D 坐标空间中的顶点。在我的例子中,我只有几个顶点来表示一个二维矩形,并且唯一要做的计算是转换为 Metal 的归一化坐标系进行渲染。 @kennyc:如果你只是加/减,那么数组结构对于 CPU SIMD 来说是很好的。操作仍然是纯垂直的(即一个向量中的第二个元素只与另一个向量中的第二个元素交互。)以上是关于解释 Metal 和 SIMD 中的不同类型的主要内容,如果未能解决你的问题,请参考以下文章
有没有像 Metal 中的 clCreateSubBuffer 一样的方法?