Last active
September 14, 2021 07:35
-
-
Save acaly/534152ea8e142a5fca78484013863c9d to your computer and use it in GitHub Desktop.
Static reflection benchmark in .NET 6.
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 BenchmarkDotNet.Analysers; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Columns; | |
using BenchmarkDotNet.Configs; | |
using BenchmarkDotNet.Diagnosers; | |
using BenchmarkDotNet.Jobs; | |
using BenchmarkDotNet.Loggers; | |
using BenchmarkDotNet.Running; | |
using System.Numerics; | |
using System.Runtime.CompilerServices; | |
namespace StaticReflectionTest | |
{ | |
using static HandlerHelper; | |
public static class HandlerHelper | |
{ | |
public static void ThrowInvalidType() | |
{ | |
throw new InvalidOperationException(); | |
} | |
} | |
public interface IConsumer1<THandlerInterface> | |
{ | |
void Consume<TField, THandler>(ref TField fieldRef) | |
where THandler : struct, IHandler<TField>, THandlerInterface; | |
} | |
public interface IStructReflection1<THandlerInterface> | |
{ | |
void Reflect<TConsumer>(ref TConsumer consumer) | |
where TConsumer : struct, IConsumer1<THandlerInterface>; | |
} | |
public interface IConsumer2<THandlerInterface> | |
{ | |
void Consume<TField, THandler>(ref TField fieldRef1, ref TField fieldRef2) | |
where THandler : struct, IHandler<TField>, THandlerInterface; | |
} | |
public interface IStructReflection2<TSelf, THandlerInterface> | |
where TSelf : IStructReflection2<TSelf, THandlerInterface> | |
{ | |
void Reflect<TConsumer>(ref TSelf other, ref TConsumer consumer) | |
where TConsumer : struct, IConsumer2<THandlerInterface>; | |
} | |
public interface IStructReflection<TSelf, THandlerInterface> | |
: IStructReflection1<THandlerInterface>, IStructReflection2<TSelf, THandlerInterface> | |
where TSelf : IStructReflection2<TSelf, THandlerInterface> | |
{ | |
} | |
public interface IHandler<T> | |
{ | |
} | |
//---- | |
public interface IEntityStateFieldHandler | |
{ | |
static abstract void ResetVelocity<T>(ref T fieldRef); | |
} | |
public struct VelocityHandler<TField> : IHandler<TField>, IEntityStateFieldHandler | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static void ResetVelocity<T>(ref T fieldRef) | |
{ | |
if (typeof(T) != typeof(TField)) | |
{ | |
ThrowInvalidType(); | |
} | |
if (typeof(TField) == typeof(Vector2)) | |
{ | |
Unsafe.As<T, Vector2>(ref fieldRef) = default; | |
} | |
else if (typeof(TField) == typeof(Vector3)) | |
{ | |
Unsafe.As<T, Vector3>(ref fieldRef) = default; | |
} | |
else | |
{ | |
ThrowInvalidType(); | |
} | |
} | |
} | |
public struct PositionHandler<TField> : IHandler<TField>, IEntityStateFieldHandler | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static void ResetVelocity<T>(ref T fieldRef) | |
{ | |
if (typeof(T) != typeof(TField)) | |
{ | |
ThrowInvalidType(); | |
} | |
if (typeof(TField) == typeof(Vector2)) | |
{ | |
} | |
else if (typeof(TField) == typeof(Vector3)) | |
{ | |
} | |
else | |
{ | |
ThrowInvalidType(); | |
} | |
} | |
} | |
public struct EntityState2D : IStructReflection<EntityState2D, IEntityStateFieldHandler> | |
{ | |
public Vector2 Position; | |
public Vector2 Velocity; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Reflect<TConsumer>(ref TConsumer consumer) | |
where TConsumer : struct, IConsumer1<IEntityStateFieldHandler> | |
{ | |
consumer.Consume<Vector2, PositionHandler<Vector2>>(ref Position); | |
consumer.Consume<Vector2, VelocityHandler<Vector2>>(ref Velocity); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Reflect<TConsumer>(ref EntityState2D other, ref TConsumer consumer) | |
where TConsumer : struct, IConsumer2<IEntityStateFieldHandler> | |
{ | |
consumer.Consume<Vector2, PositionHandler<Vector2>>(ref Position, ref other.Position); | |
consumer.Consume<Vector2, VelocityHandler<Vector2>>(ref Velocity, ref other.Velocity); | |
} | |
} | |
[Config(typeof(TestConfig))] | |
public class TestClass | |
{ | |
public class TestConfig : ManualConfig | |
{ | |
public TestConfig() | |
{ | |
AddJob(Job.Default); | |
AddLogger(ConsoleLogger.Default); | |
AddColumn(TargetMethodColumn.Method); | |
AddColumn(StatisticColumn.Mean, StatisticColumn.Median, StatisticColumn.StdDev); | |
AddAnalyser(EnvironmentAnalyser.Default); | |
UnionRule = ConfigUnionRule.AlwaysUseLocal; | |
AddDiagnoser(new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig(maxDepth: 3))); | |
} | |
} | |
private struct ResetVelocityConsumer : IConsumer1<IEntityStateFieldHandler> | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Consume<TField, THandler>(ref TField fieldRef) | |
where THandler : struct, IHandler<TField>, IEntityStateFieldHandler | |
{ | |
THandler.ResetVelocity(ref fieldRef); | |
} | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static void ResetVelocity<TState>(ref TState state) | |
where TState : struct, IStructReflection<TState, IEntityStateFieldHandler> | |
{ | |
ResetVelocityConsumer consumer = default; | |
state.Reflect(ref consumer); | |
} | |
[MethodImpl(MethodImplOptions.NoInlining)] | |
private static EntityState2D CreateState() | |
{ | |
return new() | |
{ | |
Velocity = new(1, 1), | |
Position = new(2, 2) | |
}; | |
} | |
[Benchmark] | |
public void Reflected() | |
{ | |
var state = CreateState(); | |
ResetVelocity(ref state); | |
} | |
[Benchmark] | |
public void Direct() | |
{ | |
var state = CreateState(); | |
state.Velocity = default; | |
} | |
public static void Main() | |
{ | |
BenchmarkRunner.Run<TestClass>(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment