Created
May 13, 2017 18:33
Star
You must be signed in to star a gist
Comparison of struct and class arrays in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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