Matrix 4x4对象的结构或类

时间:2014-06-15 15:50:20

标签: c# performance

我试图实现简单的软件3D引擎。所以我需要一个矩阵4x4来表示变换。我的矩阵的每个单元格类型都是System.Double格式,这意味着整个矩阵将使用8个字节* 16个单元格= 128个字节。目前我已将此作为一个类实现。此矩阵也是不可变类型 所以现在,当我试图通过这个矩阵乘以向量时,我做了类似这样的事情:matrix.m11 * vector.X(etc ...)。我认为,要访问字段m11,程序首先需要获得' matrix'参考值,然后检查它为null,然后找到m11值,是吗?如果我将类更改为struct,我的乘法会变得更快?我知道128字节结构很大,但如果我编写只使用ref / out keyowrds的方法?这将是很好的选择?
正如我在SharpDX库中看到的,矩阵是一个结构。我应该将类更改为struct然后提高性能吗?

1 个答案:

答案 0 :(得分:15)

出于以下原因,我不建议使用类来存储3D引擎的double4x4矩阵:

  • 对GC的影响:与可以存储在堆栈上或堆上对象内的值类型不同,对象实例总是在堆上分配,这会给GC带来不必要的压力。例如,如果你需要将一个World,View和Projection矩阵存储到一个对象中,你将有3个矩阵(原始数据+对象实例的成本~8-12个字节)+ 3个引用:它将创建更多的对象GC必须扫描的堆。使用对象实例有效地执行此操作需要缓存矩阵以便重用它们,而不是在将矩阵中的中间计算存储时我们使用的模式...
  • 错误的数据位置:值类型的一个重要特征是数据位置,对象实例无法保证这一点。如果将3种值类型存储到对象实例中,它们将在内存中并置,从而使缓存使用率大大提高。使用对象作为矩阵不能保证这一点,并且最有可能导致CPU cache thrashing
  • 错误互操作:3D引擎通常会将其所有计算结果发送到最有可能连接到GPU的基础3D图形层(除非您自己光栅化所有内容,这是一所古老的学校)。您经常需要向GPU发送一个float4x4数组:使用对象实例需要循环并复制到中间float4x4结构格式以复制数据,从而破坏了将float4x4作为类的全部目的。对于值类型,它只是C#中的单个fixed指令。它也适用于与其他库互操作(物理引擎需要一个float4x4结构,而不是一个对象实例)
  • 糟糕的双重:Double几乎没有在3D游戏引擎中使用,因为它们占用更多空间,因此在CPU缓存中占用更多空间。此外,处理GPU意味着您主要使用浮动。它对CPU SSE指令也不友好,这些指令针对在单个指令中执行4个浮点运算进行了优化,但在单个指令中只有2个双重运算。所以使用double来存储矩阵不是一个好主意,除非你有一个非常的理由(例如,你想在“Tightening the Precision of Perspective Rendering”时改进矩阵的计算)
  • Bad immutability:避免使用矩阵的不变性前提。除非你要在多个线程中大量浮动所有计算,否则你要确保不会搞砸事情。这种结构的不可变性是完全矫枉过正的。想象一下将转换应用于矩阵的非常简单的场景,使用mutable struct,您只需要更新一行。使用不可变结构,您需要在复制初始值时创建新的整个矩阵。

另一方面,如果你关心表现:

  • 如果您需要在类中存储valuetype矩阵,则更喜欢将它们存储在公共字段而不是公共属性中,因为它将在访问矩阵时避免不必要的副本。您还可以直接固定矩阵以将其移动到非托管内存
  • 相关的,更喜欢通过ref传递矩阵,而不是通过副本传递所有矩阵计算。