Comparison of struct and class arrays in C#
using System; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
namespace Structs_vs_classes_array_test | |
{ | |
// code slightly modified from the Jackson Dunstan's article | |
// How to Write Faster Code Than 90% of Programmers | |
// http://jacksondunstan.com/articles/3860 | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
#if DEBUG | |
Console.WriteLine("Release build might give better results"); | |
#endif | |
if (System.Diagnostics.Debugger.IsAttached) | |
Console.WriteLine("Run with Ctrl+F5 to Start without debugging"); | |
new TestScript().Start(); | |
Console.WriteLine("Done."); | |
Console.Read(); | |
} | |
} | |
class TestScript | |
{ | |
public void Start() | |
{ | |
const int count = 10 * 1000 * 1000; | |
ProjectileStruct[] projectileStructs = new ProjectileStruct[count]; | |
ProjectileClass[] projectileClasses = new ProjectileClass[count]; | |
Console.WriteLine("Creating arrays..."); | |
int[] indices = Enumerable.Range(0, count).ToArray(); | |
for (int i = 0; i < count; ++i) | |
{ | |
projectileClasses[i] = ProjectileClass.Create(); | |
projectileStructs[i] = ProjectileStruct.Create(); | |
} | |
Console.WriteLine("Shuffling arrays..."); | |
Shuffle(projectileStructs); | |
Shuffle(projectileClasses); | |
Console.WriteLine("---"); | |
Console.WriteLine("Unshuffled indices test:"); | |
RunTest(count, projectileStructs, projectileClasses, indices); | |
Shuffle(indices); | |
Console.WriteLine("---"); | |
Console.WriteLine("Shuffled indices test:"); | |
RunTest(count, projectileStructs, projectileClasses, indices); | |
} | |
private static void RunTest(int count, ProjectileStruct[] projectileStructs, ProjectileClass[] projectileClasses, int[] indices) | |
{ | |
for (int k = 0; k < 3; k++) | |
{ | |
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); | |
for (int i = 0; i < count; ++i) | |
{ | |
UpdateProjectile(ref projectileStructs[indices[i]], 0.5f); | |
} | |
sw.Stop(); | |
var structTime = sw.Elapsed.TotalMilliseconds; | |
sw.Restart(); | |
for (int i = 0; i < count; ++i) | |
{ | |
UpdateProjectile(projectileClasses[indices[i]], 0.5f); | |
} | |
sw.Stop(); | |
var classTime = sw.Elapsed.TotalMilliseconds; | |
Console.WriteLine( | |
"Struct {0:0.0}ms vs Class {1:0.0}ms ({2:0.0}x slower)", | |
structTime, | |
classTime, (double)classTime / structTime | |
); | |
} | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
static void UpdateProjectile(ref ProjectileStruct projectile, float time) | |
{ | |
projectile.Position += projectile.Velocity * time; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
static void UpdateProjectile(ProjectileClass projectile, float time) | |
{ | |
projectile.Position += projectile.Velocity * time; | |
} | |
public static void Shuffle<T>(T[] list) | |
{ | |
for (int n = list.Length; n > 1;) | |
{ | |
n--; | |
int k = TestRandom.Next(n + 1); | |
T value = list[k]; | |
list[k] = list[n]; | |
list[n] = value; | |
} | |
} | |
static Random TestRandom = new Random(); | |
struct Vector3 | |
{ | |
public float x, y, z; | |
public static Vector3 operator *(Vector3 v, float f) | |
{ | |
v.x *= f; | |
v.y *= f; | |
v.z *= f; | |
return v; | |
} | |
public static Vector3 operator +(Vector3 v, Vector3 b) | |
{ | |
v.x += b.x; | |
v.y += b.y; | |
v.z += b.z; | |
return v; | |
} | |
} | |
struct ProjectileStruct | |
{ | |
public Vector3 Position; | |
public Vector3 Velocity; | |
public static ProjectileStruct Create() | |
{ | |
var projectile = new ProjectileStruct(); | |
projectile.Position.x = (float)TestRandom.NextDouble(); | |
projectile.Position.y = (float)TestRandom.NextDouble(); | |
projectile.Position.z = (float)TestRandom.NextDouble(); | |
projectile.Velocity.x = (float)TestRandom.NextDouble(); | |
projectile.Velocity.y = (float)TestRandom.NextDouble(); | |
projectile.Velocity.z = (float)TestRandom.NextDouble(); | |
return projectile; | |
} | |
} | |
sealed class ProjectileClass | |
{ | |
public Vector3 Position; | |
public Vector3 Velocity; | |
public static ProjectileClass Create() | |
{ | |
var projectile = new ProjectileClass(); | |
projectile.Position.x = (float)TestRandom.NextDouble(); | |
projectile.Position.y = (float)TestRandom.NextDouble(); | |
projectile.Position.z = (float)TestRandom.NextDouble(); | |
projectile.Velocity.x = (float)TestRandom.NextDouble(); | |
projectile.Velocity.y = (float)TestRandom.NextDouble(); | |
projectile.Velocity.z = (float)TestRandom.NextDouble(); | |
return projectile; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment