I'm trying to implement simple software 3D engine. So I need a matrix 4x4 to represents transformations. Each cell type of my matrix is in the System.Double format which means entire matrix will use 8 bytes * 16 cells = 128 bytes. Currently I have implemented this as a class. Also this matrix is an immutable type.
So now, when I trying to multiply vector by this matrix, I'm doing something like this: matrix.m11 * vector.X (etc...). As I think, to access field m11, program first need to get 'matrix' reference value, then check it for null, then find m11 value, yes? If I would change class to struct, my multiplication coukd be alot faster? I know 128 bytes struct is big, but if I write method that works only using ref/out keyowrds? It will be good chooose?
As I see in SharpDX library, matrix is a struct. Should I change class to struct then to improve performance?
I would not recommend to use class for storing a double4x4 matrix for a 3D engine for the following reasons:
-
Bad impact on GC: Unlike value types that can be store on the stack or inside an object on the heap, object instances are always allocated on the heap and It will put unnecessary pressure on the GC. For example, if you need to store a World, View and Projection matrix into an object, you will have 3 matrices (raw datas + cost of object instance ~ 8-12 bytes) + 3 references: It will create much more objects on the heap that the GC will have to scan. Doing this efficiently with object instances would require to cache your matrices in order to reuse them, not really the kind of pattern we are using when just storing intermediate calculations on matrices...
-
Bad data locality: One important feature of value types is data locality, which is not guaranteed by object instances. If you store 3 value types into an object instance, they will be collocated in memory, resulting in much better cache usage. Using object as a matrix cannot guarantee this and will most likely results in CPU cache thrashing
-
Bad interop: A 3D engine is usually sending all its calculations to an underlying 3D graphics layer that is most likely connected to a GPU (unless you are rasterizing everything yourself, a la old school). You often need to send an array of float4x4 to the GPU: using an object instance would require a loop and a copy to an intermediate float4x4 struct format in order to copy the data, defeating the whole purpose of having float4x4 as class. With value types, it is just a single
fixed
instruction in C#. It also applies to interop to other libraries (a physic engine will expect a float4x4 struct and never an object instance)
-
Bad double: Double are barely used in a 3D game engine, as they take more space and hence eat more space in CPU cache. Also, dealing with GPU means that you are mainly working with floats. It is also not friendly with CPU SSE instructions that are well optimized for performing 4 floats ops in a single instruction, but only 2 double opts in a single instruction. So it is not a good idea to use double for storing a matrix, unless you have a very good reason (for example, you want to improve the calculation of matrix when "Tightening the Precision of Perspective Rendering")
-
Bad immutability: Avoid using the premise of immutability for matrices. Unless you are going to be massively floating around all your calculations in multiple threads and you want to be sure that you will not mess up things. Immutability for such structs is a complete overkill. Imagine the very simple scenario of applying a translation to a matrix, with a mutable struct, you just need to update a row. With an immutable struct, you need to create a new whole matrix while copying the initial values.
On a side note, if you care about performance:
- if you need to store valuetype matrix in a class, prefer to store them in a public field instead of a public property as It will avoid unnecessary copies when accessing the matrix. You will be also able to directly pin the matrix to move it to an unmanaged memory
- Related, prefer passing matrices by
ref
instead of by copy on all your matrix calculations.