-
-
Save krwq/eb06529f0c99614579f84b69720ab46e to your computer and use it in GitHub Desktop.
Setter perf comparison - code
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.Diagnostics; | |
public class Program | |
{ | |
const int Warmup = 100; | |
const int Iterations = 500000000; | |
// Comparing following approaches: | |
//Action<object, object> | |
//Action<object, TPropertyType> | |
//Action<ref TDeclaringType, TPropertyType> | |
//Func<TDeclaringType, TPropertyType, TDeclaringType> | |
public delegate void ActionDeclaringTypePropertyType<DeclaringType, PropertyType>(ref DeclaringType obj, PropertyType val); | |
static void Main() | |
{ | |
object[] intValues = new object[] { 5 }; | |
object[] stringValues = new object[] { null, "asd" }; | |
object[] structAValues = new object[] { new StructA() { Foo = 7, Bar = "ffghhjj", Baz = -1 } }; | |
object[] classAValues = new object[] { null, new ClassA() { Foo = 7, Bar = "ffghhjj", Baz = -1 } }; | |
var labelAndData = new (string, object[])[] | |
{ | |
("TestClass.IntProperty", intValues), | |
("TestClass.IntField", intValues), | |
("TestClass.StringProperty", stringValues), | |
("TestClass.StringField", stringValues), | |
("TestClass.StructProperty", structAValues), | |
("TestClass.StructField", structAValues), | |
("TestClass.ClassProperty", classAValues), | |
("TestClass.ClassField", classAValues), | |
("TestStruct.IntProperty", intValues), | |
("TestStruct.IntField", intValues), | |
("TestStruct.StringProperty", stringValues), | |
("TestStruct.StringField", stringValues), | |
("TestStruct.StructProperty", structAValues), | |
("TestStruct.StructField", structAValues), | |
("TestStruct.ClassProperty", classAValues), | |
("TestStruct.ClassField", classAValues), | |
}; | |
// declaringType | |
var actionObjObj = new (Action<object, object>, Type)[] | |
{ | |
((obj, val) => { ((TestClass)obj).IntProperty = (int)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).IntField = (int)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).StringProperty = (string)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).StringField = (string)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).StructProperty = (StructA)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).StructField = (StructA)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).ClassProperty = (ClassA)val; }, typeof(TestClass)), | |
((obj, val) => { ((TestClass)obj).ClassField = (ClassA)val; }, typeof(TestClass)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).IntProperty = (int)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).IntField = (int)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StringProperty = (string)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StringField = (string)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StructProperty = (StructA)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StructField = (StructA)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).ClassProperty = (ClassA)val; }, typeof(TestStruct)), | |
((obj, val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).ClassField = (ClassA)val; }, typeof(TestStruct)), | |
}; | |
// declaringType, propertyType | |
var actionObjPropertyType = new ( object, Type, Type)[] | |
{ | |
((object obj, int val) => { ((TestClass)obj).IntProperty = val; }, typeof(TestClass), typeof(int)), | |
((object obj, int val) => { ((TestClass)obj).IntField = val; }, typeof(TestClass), typeof(int)), | |
((object obj, string val) => { ((TestClass)obj).StringProperty = val; }, typeof(TestClass), typeof(string)), | |
((object obj, string val) => { ((TestClass)obj).StringField = val; }, typeof(TestClass), typeof(string)), | |
((object obj, StructA val) => { ((TestClass)obj).StructProperty = val; }, typeof(TestClass), typeof(StructA)), | |
((object obj, StructA val) => { ((TestClass)obj).StructField = val; }, typeof(TestClass), typeof(StructA)), | |
((object obj, ClassA val) => { ((TestClass)obj).ClassProperty = val; }, typeof(TestClass), typeof(ClassA)), | |
((object obj, ClassA val) => { ((TestClass)obj).ClassField = val; }, typeof(TestClass), typeof(ClassA)), | |
((object obj, int val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).IntProperty = val; }, typeof(TestStruct), typeof(int)), | |
((object obj, int val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).IntField = val; }, typeof(TestStruct), typeof(int)), | |
((object obj, string val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StringProperty = val; }, typeof(TestStruct), typeof(string)), | |
((object obj, string val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StringField = val; }, typeof(TestStruct), typeof(string)), | |
((object obj, StructA val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StructProperty = val; }, typeof(TestStruct), typeof(StructA)), | |
((object obj, StructA val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).StructField = val; }, typeof(TestStruct), typeof(StructA)), | |
((object obj, ClassA val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).ClassProperty = val; }, typeof(TestStruct), typeof(ClassA)), | |
((object obj, ClassA val) => { System.Runtime.CompilerServices.Unsafe.Unbox<TestStruct>(obj).ClassField = val; }, typeof(TestStruct), typeof(ClassA)), | |
}; | |
// declaringType, propertyType | |
var actionDeclaringTypePropertyType = new (object, Type, Type)[] | |
{ | |
(new ActionDeclaringTypePropertyType<TestClass, int>((ref TestClass obj, int val) => { obj.IntProperty = val; }), typeof(TestClass), typeof(int)), | |
(new ActionDeclaringTypePropertyType<TestClass, int>((ref TestClass obj, int val) => { obj.IntField = val; }), typeof(TestClass), typeof(int)), | |
(new ActionDeclaringTypePropertyType<TestClass, string>((ref TestClass obj, string val) => { obj.StringProperty = val; }), typeof(TestClass), typeof(string)), | |
(new ActionDeclaringTypePropertyType<TestClass, string>((ref TestClass obj, string val) => { obj.StringField = val; }), typeof(TestClass), typeof(string)), | |
(new ActionDeclaringTypePropertyType<TestClass, StructA>((ref TestClass obj, StructA val) => { obj.StructProperty = val; }), typeof(TestClass), typeof(StructA)), | |
(new ActionDeclaringTypePropertyType<TestClass, StructA>((ref TestClass obj, StructA val) => { obj.StructField = val; }), typeof(TestClass), typeof(StructA)), | |
(new ActionDeclaringTypePropertyType<TestClass, ClassA>((ref TestClass obj, ClassA val) => { obj.ClassProperty = val; }), typeof(TestClass), typeof(ClassA)), | |
(new ActionDeclaringTypePropertyType<TestClass, ClassA>((ref TestClass obj, ClassA val) => { obj.ClassField = val; }), typeof(TestClass), typeof(ClassA)), | |
(new ActionDeclaringTypePropertyType<TestStruct, int>((ref TestStruct obj, int val) => { obj.IntProperty = val; }), typeof(TestStruct), typeof(int)), | |
(new ActionDeclaringTypePropertyType<TestStruct, int>((ref TestStruct obj, int val) => { obj.IntField = val; }), typeof(TestStruct), typeof(int)), | |
(new ActionDeclaringTypePropertyType<TestStruct, string>((ref TestStruct obj, string val) => { obj.StringProperty = val; }), typeof(TestStruct), typeof(string)), | |
(new ActionDeclaringTypePropertyType<TestStruct, string>((ref TestStruct obj, string val) => { obj.StringField = val; }), typeof(TestStruct), typeof(string)), | |
(new ActionDeclaringTypePropertyType<TestStruct, StructA>((ref TestStruct obj, StructA val) => { obj.StructProperty = val; }), typeof(TestStruct), typeof(StructA)), | |
(new ActionDeclaringTypePropertyType<TestStruct, StructA>((ref TestStruct obj, StructA val) => { obj.StructField = val; }), typeof(TestStruct), typeof(StructA)), | |
(new ActionDeclaringTypePropertyType<TestStruct, ClassA>((ref TestStruct obj, ClassA val) => { obj.ClassProperty = val; }), typeof(TestStruct), typeof(ClassA)), | |
(new ActionDeclaringTypePropertyType<TestStruct, ClassA>((ref TestStruct obj, ClassA val) => { obj.ClassField = val; }), typeof(TestStruct), typeof(ClassA)), | |
}; | |
// declaringType, propertyType | |
var funcDeclaringTypePropertyType = new (object, Type, Type)[] | |
{ | |
((TestClass obj, int val) => { obj.IntProperty = val; return obj; }, typeof(TestClass), typeof(int)), | |
((TestClass obj, int val) => { obj.IntField = val; return obj; }, typeof(TestClass), typeof(int)), | |
((TestClass obj, string val) => { obj.StringProperty = val; return obj; }, typeof(TestClass), typeof(string)), | |
((TestClass obj, string val) => { obj.StringField = val; return obj; }, typeof(TestClass), typeof(string)), | |
((TestClass obj, StructA val) => { obj.StructProperty = val; return obj; }, typeof(TestClass), typeof(StructA)), | |
((TestClass obj, StructA val) => { obj.StructField = val; return obj; }, typeof(TestClass), typeof(StructA)), | |
((TestClass obj, ClassA val) => { obj.ClassProperty = val; return obj; }, typeof(TestClass), typeof(ClassA)), | |
((TestClass obj, ClassA val) => { obj.ClassField = val; return obj; }, typeof(TestClass), typeof(ClassA)), | |
((TestStruct obj, int val) => { obj.IntProperty = val; return obj; }, typeof(TestStruct), typeof(int)), | |
((TestStruct obj, int val) => { obj.IntField = val; return obj; }, typeof(TestStruct), typeof(int)), | |
((TestStruct obj, string val) => { obj.StringProperty = val; return obj; }, typeof(TestStruct), typeof(string)), | |
((TestStruct obj, string val) => { obj.StringField = val; return obj; }, typeof(TestStruct), typeof(string)), | |
((TestStruct obj, StructA val) => { obj.StructProperty = val; return obj; }, typeof(TestStruct), typeof(StructA)), | |
((TestStruct obj, StructA val) => { obj.StructField = val; return obj; }, typeof(TestStruct), typeof(StructA)), | |
((TestStruct obj, ClassA val) => { obj.ClassProperty = val; return obj; }, typeof(TestStruct), typeof(ClassA)), | |
((TestStruct obj, ClassA val) => { obj.ClassField = val; return obj; }, typeof(TestStruct), typeof(ClassA)), | |
}; | |
Console.WriteLine($"Times in milliseconds. Iterations per testcase: {Iterations}"); | |
Console.WriteLine(); | |
Console.WriteLine("| TestCase | `Action<object, object>` | `Action<object, TPropertyType>` | `Actionish<ref TDeclaringType, TPropertyType>` | `Func<TDeclaringType, TPropertyType, TDeclaringType>` |"); | |
Console.WriteLine("| --- | --- | --- | --- | --- |"); | |
for (int i = 0; i < labelAndData.Length; i++) | |
{ | |
(var label, var exampleData) = labelAndData[i]; | |
Stopwatch[] measurements = new Stopwatch[4]; | |
measurements[0] = TestActionObjObjTD(actionObjObj[i], exampleData); | |
measurements[1] = TestActionObjPropertyTypeTD(actionObjPropertyType[i], exampleData); | |
measurements[2] = TestActionDeclaringTypePropertyTypeTD(actionDeclaringTypePropertyType[i], exampleData); | |
measurements[3] = TestFuncDeclaringTypePropertyTypeTD(funcDeclaringTypePropertyType[i], exampleData); | |
Console.Write($"| {label} |"); | |
Stopwatch baseline = null; | |
foreach (var meas in measurements) | |
{ | |
ShowMeasurement(meas, baseline); | |
baseline ??= meas; // first element | |
} | |
Console.WriteLine(); | |
} | |
} | |
private static void ShowMeasurement(Stopwatch measurement, Stopwatch baseline) | |
{ | |
long totalMs = measurement.ElapsedMilliseconds; | |
Console.Write($" {totalMs} "); | |
if (baseline != null) | |
{ | |
long baselineMs = baseline.ElapsedMilliseconds; | |
double diffPercentage = totalMs * 100.0 / baselineMs - 100.0; | |
Console.Write($"[{diffPercentage:+0.##;-0.##}%] "); | |
} | |
Console.Write("|"); | |
} | |
private static Stopwatch TestActionObjObjTD((Action<object, object> setter, Type declaringType) d, object[] exampleValues) | |
{ | |
object obj = Activator.CreateInstance(d.declaringType); | |
return TestActionObjObj(d.setter, obj, exampleValues); | |
} | |
private static Stopwatch TestActionObjPropertyTypeTD((object setter, Type declaringType, Type propertyType) d, object[] exampleValues) | |
{ | |
object obj = Activator.CreateInstance(d.declaringType); | |
return (Stopwatch)typeof(Program).GetMethod(nameof(TestActionObjPropertyType)).MakeGenericMethod(d.propertyType).Invoke(null, new object[] { d.setter, obj, exampleValues }); | |
} | |
private static Stopwatch TestActionDeclaringTypePropertyTypeTD((object setter, Type declaringType, Type propertyType) d, object[] exampleValues) | |
{ | |
object obj = Activator.CreateInstance(d.declaringType); | |
return (Stopwatch)typeof(Program).GetMethod(nameof(TestActionDeclaringTypePropertyType)).MakeGenericMethod(d.declaringType, d.propertyType).Invoke(null, new object[] { d.setter, obj, exampleValues }); | |
} | |
private static Stopwatch TestFuncDeclaringTypePropertyTypeTD((object setter, Type declaringType, Type propertyType) d, object[] exampleValues) | |
{ | |
object obj = Activator.CreateInstance(d.declaringType); | |
return (Stopwatch)typeof(Program).GetMethod(nameof(TestFuncDeclaringTypePropertyType)).MakeGenericMethod(d.declaringType, d.propertyType).Invoke(null, new object[] { d.setter, obj, exampleValues }); | |
} | |
private static PropertyType[] CastObjects<PropertyType>(object[] objValues) | |
{ | |
PropertyType[] values = new PropertyType[objValues.Length]; | |
for (int i = 0; i < values.Length; i++) | |
{ | |
values[i] = (PropertyType)objValues[i]; | |
} | |
return values; | |
} | |
public static Stopwatch TestActionObjObj(Action<object, object> setter, object targetObject, object[] values) | |
{ | |
Stopwatch sw = Stopwatch.StartNew(); | |
for (int i = 0; i < Warmup; i++) | |
{ | |
setter(targetObject, values[i % values.Length]); | |
} | |
// in case JITTing takes some time | |
sw.Restart(); | |
sw.Stop(); | |
sw.Restart(); | |
for (int i = 0; i < Iterations; i++) | |
{ | |
setter(targetObject, values[i % values.Length]); | |
} | |
sw.Stop(); | |
return sw; | |
} | |
public static Stopwatch TestActionObjPropertyType<PropertyType>(Action<object, PropertyType> setter, object targetObject, object[] objValues) | |
{ | |
PropertyType[] values = CastObjects<PropertyType>(objValues); | |
Stopwatch sw = Stopwatch.StartNew(); | |
for (int i = 0; i < Warmup; i++) | |
{ | |
setter(targetObject, values[i % values.Length]); | |
} | |
// in case JITTing takes some time | |
sw.Restart(); | |
sw.Stop(); | |
sw.Restart(); | |
for (int i = 0; i < Iterations; i++) | |
{ | |
setter(targetObject, values[i % values.Length]); | |
} | |
sw.Stop(); | |
return sw; | |
} | |
public static Stopwatch TestActionDeclaringTypePropertyType<DeclaringType, PropertyType>(ActionDeclaringTypePropertyType<DeclaringType, PropertyType> setter, DeclaringType targetObject, object[] objValues) | |
{ | |
PropertyType[] values = CastObjects<PropertyType>(objValues); | |
Stopwatch sw = Stopwatch.StartNew(); | |
for (int i = 0; i < Warmup; i++) | |
{ | |
setter(ref targetObject, values[i % values.Length]); | |
} | |
// in case JITTing takes some time | |
sw.Restart(); | |
sw.Stop(); | |
sw.Restart(); | |
for (int i = 0; i < Iterations; i++) | |
{ | |
setter(ref targetObject, values[i % values.Length]); | |
} | |
sw.Stop(); | |
return sw; | |
} | |
public static Stopwatch TestFuncDeclaringTypePropertyType<DeclaringType, PropertyType>(Func<DeclaringType, PropertyType, DeclaringType> setter, DeclaringType targetObject, object[] objValues) | |
{ | |
PropertyType[] values = CastObjects<PropertyType>(objValues); | |
Stopwatch sw = Stopwatch.StartNew(); | |
for (int i = 0; i < Warmup; i++) | |
{ | |
targetObject = setter(targetObject, values[i % values.Length]); | |
} | |
// in case JITTing takes some time | |
sw.Restart(); | |
sw.Stop(); | |
sw.Restart(); | |
for (int i = 0; i < Iterations; i++) | |
{ | |
targetObject = setter(targetObject, values[i % values.Length]); | |
} | |
sw.Stop(); | |
return sw; | |
} | |
} | |
public class TestClass | |
{ | |
public int IntProperty { get; set; } | |
public int IntField; | |
public string StringProperty { get; set; } | |
public string StringField; | |
public StructA StructProperty { get; set; } | |
public StructA StructField; | |
public ClassA ClassProperty { get; set; } | |
public ClassA ClassField; | |
} | |
public struct TestStruct | |
{ | |
public int IntProperty { get; set; } | |
public int IntField; | |
public string StringProperty { get; set; } | |
public string StringField; | |
public StructA StructProperty { get; set; } | |
public StructA StructField; | |
public ClassA ClassProperty { get; set; } | |
public ClassA ClassField; | |
} | |
public struct StructA | |
{ | |
public int Foo; | |
public string Bar; | |
public int Baz; | |
} | |
public class ClassA | |
{ | |
public int Foo; | |
public string Bar; | |
public int Baz; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment