Skip to content

Instantly share code, notes, and snippets.

@acaly
Last active September 14, 2021 07:35
Show Gist options
  • Save acaly/534152ea8e142a5fca78484013863c9d to your computer and use it in GitHub Desktop.
Save acaly/534152ea8e142a5fca78484013863c9d to your computer and use it in GitHub Desktop.
Static reflection benchmark in .NET 6.
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