Skip to content

Instantly share code, notes, and snippets.

@krwq
Created April 22, 2022 11:56
Show Gist options
  • Save krwq/eb06529f0c99614579f84b69720ab46e to your computer and use it in GitHub Desktop.
Save krwq/eb06529f0c99614579f84b69720ab46e to your computer and use it in GitHub Desktop.
Setter perf comparison - code
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