OK this is my test harness. My project properties are Release Build, "optimize code" and also "Allow unsafe code" checked.
Surprisingly (to me anyway) the performance is very different inside and outside the IDE. When run from the IDE there are noticeable differences (and the x64 difference is huge). When run outside the IDE, it's a wash.
So this is kind of weird, and I can't explain the results for IDE+x64. Maybe this is interesting to some people, but because it no longer purports to provide an answer to the poster's original question, maybe this should be moved to some other topic?
Inside IDE, platform set to x86
pass 1: old 00:00:09.7505625 new 00:00:08.6897013 percent 0.1088
Inside IDE, platform set to x64
pass 1: old 00:00:14.7584514 new 00:00:08.8835715 percent 0.398068858362741
Running from command line, platform set to x86
pass 1: old 00:00:07.6576469 new 00:00:07.2818252 percent 0.0490779615341104
Running from command line, platform set to x64
pass 1: old 00:00:07.2501032 new 00:00:07.3077479 percent -0.00795087992678504
And this is the code:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication23 {
public class Program {
public static void Main() {
const int repeatCount=20;
const int arraySize=5000000;
var values=MakeValues(arraySize);
for(var pass=0; pass<2; ++pass) {
Console.WriteLine("Starting old");
var startOld=DateTime.Now;
for(var i=0; i<repeatCount; ++i) {
var result=TransformOld(values);
}
var elapsedOld=DateTime.Now-startOld;
Console.WriteLine("Starting new");
var startNew=DateTime.Now;
for(var i=0; i<repeatCount; ++i) {
var result=TransformNew(values);
}
var elapsedNew=DateTime.Now-startNew;
var difference=elapsedOld-elapsedNew;
var percentage=(double)difference.TotalMilliseconds/elapsedOld.TotalMilliseconds;
Console.WriteLine("pass {0}: old {1} new {2} percent {3}", pass, elapsedOld, elapsedNew, percentage);
}
Console.Write("Press enter: ");
Console.ReadLine();
}
private static float4x4[] MakeValues(int count) {
var result=new float4x4[count];
for(var i=0; i<count; ++i) {
result[i]=new float4x4(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
}
return result;
}
public static float[] TransformOld(float4x4[] value) {
var array=new float[value.Length*16];
int n = 0;
for(int i = 0; i < value.Length; i++) {
array[n++] = value[i].M11;
array[n++] = value[i].M12;
array[n++] = value[i].M13;
array[n++] = value[i].M14;
array[n++] = value[i].M21;
array[n++] = value[i].M22;
array[n++] = value[i].M23;
array[n++] = value[i].M24;
array[n++] = value[i].M31;
array[n++] = value[i].M32;
array[n++] = value[i].M33;
array[n++] = value[i].M34;
array[n++] = value[i].M41;
array[n++] = value[i].M42;
array[n++] = value[i].M43;
array[n++] = value[i].M44;
}
return array;
}
public static unsafe float[] TransformNew(float4x4[] values) {
var array=new float[values.Length*16];
fixed(float* arrayStart=array) {
var destp=arrayStart;
fixed(float4x4* valuesStart=values) {
int count=values.Length;
for(var valuesp=valuesStart; count>0; ++valuesp, --count) {
var sourcep=valuesp->data;
for(var i=0; i<16/4; ++i) {
*destp++=*sourcep++;
*destp++=*sourcep++;
*destp++=*sourcep++;
*destp++=*sourcep++;
}
}
}
return array;
}
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct float4x4 {
[FieldOffset(0)] public float M11;
[FieldOffset(4)] public float M12;
[FieldOffset(8)] public float M13;
[FieldOffset(12)] public float M14;
[FieldOffset(16)] public float M21;
[FieldOffset(20)] public float M22;
[FieldOffset(24)] public float M23;
[FieldOffset(28)] public float M24;
[FieldOffset(32)] public float M31;
[FieldOffset(36)] public float M32;
[FieldOffset(40)] public float M33;
[FieldOffset(44)] public float M34;
[FieldOffset(48)] public float M41;
[FieldOffset(52)] public float M42;
[FieldOffset(56)] public float M43;
[FieldOffset(60)] public float M44;
//notice the use of "fixed" keyword to make the array inline
//and the use of the FieldOffset attribute to overlay that inline array on top of the other fields
[FieldOffset(0)] public fixed float data[16];
public float4x4(float m11, float m12, float m13, float m14,
float m21, float m22, float m23, float m24,
float m31, float m32, float m33, float m34,
float m41, float m42, float m43, float m44) {
M11=m11; M12=m12; M13=m13; M14=m14;
M21=m21; M22=m22; M23=m23; M24=m24;
M31=m31; M32=m32; M33=m33; M34=m34;
M41=m41; M42=m42; M43=m43; M44=m44;
}
}
}
}