using System; using System.Collections; using System.Runtime.CompilerServices; using LitMotion.Adapters; using NUnit.Framework; using Unity.PerformanceTesting; using UnityEngine.TestTools; namespace LitMotion.Tests.Benchmark { public class BindBenchmark { CompositeMotionHandle handles = new(25000); [OneTimeSetUp] public void OneTimeSetUp() { MotionDispatcher.EnsureStorageCapacity(64000); } [TearDown] public void TearDown() { handles.Cancel(); } [Test] [Performance] public void Benchmark_Bind_Box_StartUp() { BenchmarkHelper.MeasureStartUp(() => { int state = 0; LMotion.Create(0f, 1f, 3f) .Bind(Box.Create(state), (x, box) => { DoNothing(x, box.Value); }) .AddTo(handles); }, () => handles.Cancel()); } [Test] [Performance] public void Benchmark_Bind_ActionWithState_StartUp() { BenchmarkHelper.MeasureStartUp(() => { int state = 0; LMotion.Create(0f, 1f, 3f) .Bind(new ActionWithState(state, static (x, state) => DoNothing(x, state)), (x, state) => state.Invoke(x)) .AddTo(handles); }, () => handles.Cancel()); } [Test] [Performance] public void Benchmark_Bind_WrappedAction_StartUp() { BenchmarkHelper.MeasureStartUp(() => { int state = 0; LMotion.Create(0f, 1f, 3f) .Bind(Box.Create(state), static (x, state) => DoNothing(x, state), (float x, Box box, Action action) => action(x, box.Value)) .AddTo(handles); }, () => handles.Cancel()); } [Test] [Performance] public void Benchmark_Bind_Closure_Startup() { BenchmarkHelper.MeasureStartUp(() => { int state = 0; LMotion.Create(0f, 1f, 3f) .Bind(x => { DoNothing(x, state); }) .AddTo(handles); }, () => handles.Cancel()); } [UnityTest] [Performance] public IEnumerator Benchmark_Bind_Box_Update() { return BenchmarkHelper.MeasureUpdate(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(Box.Create(i), (x, state) => { DoNothing(x, state.Value); }) .AddTo(handles); } }); } [UnityTest] [Performance] public IEnumerator Benchmark_Bind_ActionWithState_Update() { return BenchmarkHelper.MeasureUpdate(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(new ActionWithState(i, static (x, state) => DoNothing(x, state)), (x, action) => action.Invoke(x)) .AddTo(handles); } }); } [UnityTest] [Performance] public IEnumerator Benchmark_Bind_WrappedAction_Update() { return BenchmarkHelper.MeasureUpdate(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(Box.Create(i), static (x, state) => DoNothing(x, state), (float x, Box box, Action action) => action(x, box.Value)) .AddTo(handles); } }); } [UnityTest] [Performance] public IEnumerator Benchmark_Bind_Closure_Update() { return BenchmarkHelper.MeasureUpdate(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(x => { DoNothing(x, i); }) .AddTo(handles); } }); } [Test] [Performance] public void Benchmark_Bind_Box_GC() { BenchmarkHelper.MeasureGC(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(Box.Create(i), (x, i) => { DoNothing(x, i.Value); }) .AddTo(handles); } }); handles.Cancel(); } [Test] [Performance] public void Benchmark_Bind_WrappedAction_GC() { BenchmarkHelper.MeasureGC(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(Box.Create(i), static (x, state) => DoNothing(x, state), (float x, Box box, Action action) => action(x, box.Value)) .AddTo(handles); } }); handles.Cancel(); } [Test] [Performance] public void Benchmark_Bind_ActionWithState_GC() { BenchmarkHelper.MeasureGC(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(new ActionWithState(i, static (x, state) => DoNothing(x, state)), (x, action) => action.Invoke(x)) .AddTo(handles); } }); handles.Cancel(); } [Test] [Performance] public void Benchmark_Bind_Closure_GC() { BenchmarkHelper.MeasureGC(() => { for (int i = 0; i < 10000; i++) { LMotion.Create(0f, 1f, 3f) .Bind(x => { DoNothing(x, i); }) .AddTo(handles); } }); handles.Cancel(); } [MethodImpl(MethodImplOptions.NoInlining)] static void DoNothing(float value, int state) { } static void BindAction(float value, Box state, Action action) { action(value, state.Value); } } internal sealed class ActionWithState where TValue : unmanaged where TState : struct { public ActionWithState(TState state, Action action) { this.state = state; this.action = action; } readonly TState state; readonly Action action; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Invoke(TValue value) { action(value, state); } } }