diff --git a/.gitignore b/.gitignore
index 74a2f8d0c3..b6ea476bfe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,6 +83,9 @@ user-token.json
/cli/tests/bin/
/cli/tests/obj/
/cli/tests/.idea/
+/cli/benchmarks/bin/
+/cli/benchmarks/obj/
+/cli/benchmarks/.idea/
/cli/*.sln.DotSettings.*
cli/cli/beamoLocalRuntime.json
diff --git a/cli/Benchmarks/Benchmarks.csproj b/cli/Benchmarks/Benchmarks.csproj
new file mode 100644
index 0000000000..1838a372fb
--- /dev/null
+++ b/cli/Benchmarks/Benchmarks.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0
+ enable
+ enable
+ Exe
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cli/Benchmarks/DependencyProviderBenchmarks.cs b/cli/Benchmarks/DependencyProviderBenchmarks.cs
new file mode 100644
index 0000000000..aa00f03ef9
--- /dev/null
+++ b/cli/Benchmarks/DependencyProviderBenchmarks.cs
@@ -0,0 +1,67 @@
+using Beamable.Common.Dependencies;
+using BenchmarkDotNet.Attributes;
+using System.Collections.Concurrent;
+
+namespace Benchmarks;
+
+[MemoryDiagnoser]
+[ShortRunJob]
+public class DependencyProviderBenchmarks
+{
+ // [Benchmark]
+ // public void JustAService()
+ // {
+ // var x = new ServiceDescriptor();
+ // }
+ // [Benchmark]
+ // public void Dict_Concurrent()
+ // {
+ // var x = new ConcurrentDictionary();
+ // }
+ // [Benchmark]
+ // public void Dict_Regular()
+ // {
+ // var x = new Dictionary();
+ // }
+
+ // [Benchmark]
+ public void BaseCase_JustProvider()
+ {
+ var provider = new DependencyProvider(null, null);
+ }
+
+ [Benchmark]
+ public void BaseCase_NoDispose()
+ {
+ var builder = new DependencyBuilder();
+ // builder.AddSingleton();
+ var provider = builder.Build();
+
+ // var service = provider.GetService();
+ }
+ //
+ // [Benchmark]
+ public void BaseCase_NoDispose_RegisterAndResolve()
+ {
+ var builder = new DependencyBuilder();
+ // builder.AddSingleton();
+ var provider = builder.Build();
+ // var service = provider.GetService();
+ // var serv = new TestService();
+ }
+ //
+ //
+ [Benchmark]
+ public void BaseCase_Dispose()
+ {
+ var builder = new DependencyBuilder();
+ var provider = builder.Build();
+ provider.Dispose();
+ }
+
+
+ public class TestService
+ {
+
+ }
+}
diff --git a/cli/Benchmarks/MultiThreadedAccessBenchmarks.cs b/cli/Benchmarks/MultiThreadedAccessBenchmarks.cs
new file mode 100644
index 0000000000..700a45a812
--- /dev/null
+++ b/cli/Benchmarks/MultiThreadedAccessBenchmarks.cs
@@ -0,0 +1,37 @@
+using Beamable.Common.Dependencies;
+using BenchmarkDotNet.Attributes;
+
+namespace Benchmarks;
+
+[MemoryDiagnoser]
+[ShortRunJob]
+public class MultiThreadedAccessBenchmarks
+{
+ [Benchmark]
+ public async Task SingletonOnlyGetsMadeOnce()
+ {
+ var builder = new DependencyBuilder();
+ int count = 0;
+ builder.AddSingleton(_ =>
+ {
+ count++;
+ return new A();
+ });
+ var provider = builder.Build();
+
+ var tasks = Enumerable.Range(0, 10_000).Select(i => Task.Run(async () =>
+ {
+ await Task.Delay(1);
+ provider.GetService();
+ }));
+
+ await Task.WhenAll(tasks);
+
+
+ }
+
+ public class A
+ {
+ // no-op class just for testing
+ }
+}
diff --git a/cli/Benchmarks/Program.cs b/cli/Benchmarks/Program.cs
new file mode 100644
index 0000000000..6e4feb1faa
--- /dev/null
+++ b/cli/Benchmarks/Program.cs
@@ -0,0 +1,14 @@
+using BenchmarkDotNet.Running;
+
+namespace Benchmarks;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ // BenchmarkRunner.Run();
+ BenchmarkRunner.Run();
+ // BenchmarkRunner.Run();
+
+ }
+}
diff --git a/cli/Benchmarks/PromiseBenchmarks.cs b/cli/Benchmarks/PromiseBenchmarks.cs
new file mode 100644
index 0000000000..3cf584fbca
--- /dev/null
+++ b/cli/Benchmarks/PromiseBenchmarks.cs
@@ -0,0 +1,94 @@
+using Beamable.Common;
+using BenchmarkDotNet.Attributes;
+using System.Runtime.InteropServices.JavaScript;
+
+namespace Benchmarks;
+
+[MemoryDiagnoser]
+[ShortRunJob]
+public class PromiseBenchmarks
+{
+
+ // [GlobalSetup]
+ public void Setup()
+ {
+ var x = PromiseBase.Unit;
+ }
+ // [Benchmark]
+ public Promise PromiseComplete()
+ {
+ var p = new Promise();
+ //p.CompleteSuccess();
+ return p;
+ }
+ // [Benchmark]
+ public void PromiseAllocation_Many()
+ {
+ for (var i = 0; i < 10_000; i++)
+ {
+ var p = new Promise();
+ }
+ }
+
+ [Benchmark]
+ public async Task Await()
+ {
+ var p = new Promise();
+ p.CompleteSuccess(3);
+
+ return await p;
+ }
+
+ // [Benchmark]
+ public Promise PromiseAllocation()
+ {
+ var p = new Promise();
+ return p;
+ }
+
+
+ // [Benchmark]
+ public async Promise ReturnAsyncPromise()
+ {
+ // var p = new Promise();
+
+ }
+
+
+ // [Benchmark]
+ public async Promise AsyncAwait2()
+ {
+ var p = new Promise();
+ p.CompleteSuccess();
+ }
+
+ // [Benchmark]
+ public async Task Sequence()
+ {
+ var pList = Enumerable.Range(0, 10).Select(_ => new Promise()).ToList();
+ var final = Promise.Sequence(pList);
+
+ var _ = pList.Select(p => Task.Run(async () =>
+ {
+ await Task.Delay(1);
+ p.CompleteSuccess(1);
+ })).ToList();
+
+ await final;
+ }
+
+ // [Benchmark]
+ public async Task WhenAll()
+ {
+ var pList = Enumerable.Range(0, 10).Select(_ => new Promise()).ToList();
+ var final = Promise.WhenAll(pList);
+
+ var _ = pList.Select(p => Task.Run(async () =>
+ {
+ await Task.Delay(1);
+ p.CompleteSuccess(1);
+ })).ToList();
+
+ await final;
+ }
+}
diff --git a/cli/beamable.common/Runtime/Dependencies/DependencyBuilder.cs b/cli/beamable.common/Runtime/Dependencies/DependencyBuilder.cs
index 77176ff41c..875bbf2285 100644
--- a/cli/beamable.common/Runtime/Dependencies/DependencyBuilder.cs
+++ b/cli/beamable.common/Runtime/Dependencies/DependencyBuilder.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
@@ -8,10 +9,10 @@ namespace Beamable.Common.Dependencies
public enum DependencyLifetime
{
- Unknown,
- Transient,
- Scoped,
- Singleton
+ Unknown = 1,
+ Transient = 2,
+ Scoped = 3,
+ Singleton = 4
}
///
@@ -449,18 +450,21 @@ public class BuildOptions
///
public class DependencyBuilder : IDependencyBuilder
{
- public List TransientServices { get; protected set; } = new List();
- public List ScopedServices { get; protected set; } = new List();
- public List SingletonServices { get; protected set; } = new List();
+ // public List TransientServices { get; protected set; } = new List();
+ // public List ScopedServices { get; protected set; } = new List();
+ // public List SingletonServices { get; protected set; } = new List();
- List IDependencyBuilder.GetTransientServices() => TransientServices;
- List IDependencyBuilder.GetScopedServices() => ScopedServices;
- List IDependencyBuilder.GetSingletonServices() => SingletonServices;
+ public List Descriptors { get; protected set; } = new List();
+
+ List IDependencyBuilder.GetTransientServices() => Descriptors.Where(x => x.Lifetime == DependencyLifetime.Transient).ToList();
+ List IDependencyBuilder.GetScopedServices() => Descriptors.Where(x => x.Lifetime == DependencyLifetime.Scoped).ToList();
+ List IDependencyBuilder.GetSingletonServices() => Descriptors.Where(x => x.Lifetime == DependencyLifetime.Singleton).ToList();
public IDependencyBuilder AddTransient(Func factory) where TImpl : TInterface
{
- TransientServices.Add(new ServiceDescriptor
+ Descriptors.Add(new ServiceDescriptor
{
+ Lifetime = DependencyLifetime.Transient,
Interface = typeof(TInterface),
Implementation = typeof(TImpl),
Factory = (provider) => factory(provider)
@@ -482,8 +486,9 @@ public IDependencyBuilder AddTransient() where TImpl : TInter
public IDependencyBuilder AddScoped(Func factory) where TImpl : TInterface
{
- ScopedServices.Add(new ServiceDescriptor
+ Descriptors.Add(new ServiceDescriptor
{
+ Lifetime = DependencyLifetime.Scoped,
Interface = typeof(TInterface),
Implementation = typeof(TImpl),
Factory = (provider) => factory(provider)
@@ -511,8 +516,9 @@ public IDependencyBuilder AddScoped() where TImpl : TInterfac
public IDependencyBuilder AddSingleton(Func factory) where TImpl : TInterface
{
- SingletonServices.Add(new ServiceDescriptor
+ Descriptors.Add(new ServiceDescriptor
{
+ Lifetime = DependencyLifetime.Singleton,
Interface = typeof(TInterface),
Implementation = typeof(TImpl),
Factory = (provider) => factory(provider)
@@ -522,8 +528,9 @@ public IDependencyBuilder AddSingleton(Func Instantiate(type, provider)
@@ -534,8 +541,9 @@ public IDependencyBuilder AddSingleton(Type type)
public IDependencyBuilder AddSingleton(Type registeringType, Func factory)
{
System.Diagnostics.Debug.Assert(typeof(T).IsAssignableFrom(registeringType), $"RegisteringType [{registeringType.Name}] does not implement/inherit from [{typeof(T).Name}]!");
- SingletonServices.Add(new ServiceDescriptor
+ Descriptors.Add(new ServiceDescriptor
{
+ Lifetime = DependencyLifetime.Singleton,
Interface = registeringType,
Implementation = registeringType,
Factory = (provider) => factory()
@@ -584,8 +592,9 @@ public IDependencyBuilder ReplaceSingleton(Func();
}
- SingletonServices.Add(new ServiceDescriptor
+ Descriptors.Add(new ServiceDescriptor
{
+ Lifetime = DependencyLifetime.Singleton,
Interface = typeof(TExisting),
Implementation = typeof(TNew),
Factory = provider => factory(provider)
@@ -677,37 +686,56 @@ public IDependencyProviderScope Build(BuildOptions options = null)
public IDependencyBuilder Remove()
{
- if (TryGetTransient(typeof(T), out var transient))
+ // foreach (var service in Descriptors)
+ for (var i = 0 ; i < Descriptors.Count; i ++)
{
- TransientServices.Remove(transient);
- return this;
- }
-
- if (TryGetScoped(typeof(T), out var scoped))
- {
- ScopedServices.Remove(scoped);
- return this;
- }
-
- if (TryGetSingleton(typeof(T), out var singleton))
- {
- SingletonServices.Remove(singleton);
- return this;
+ if (Descriptors[i].Interface == typeof(T))
+ {
+ Descriptors.RemoveAt(i);
+ return this;
+ }
}
+
+ // if (TryGetTransient(typeof(T), out var transient))
+ // {
+ // TransientServices.Remove(transient);
+ // return this;
+ // }
+ //
+ // if (TryGetScoped(typeof(T), out var scoped))
+ // {
+ // ScopedServices.Remove(scoped);
+ // return this;
+ // }
+ //
+ // if (TryGetSingleton(typeof(T), out var singleton))
+ // {
+ // SingletonServices.Remove(singleton);
+ // return this;
+ // }
throw new Exception($"Service does not exist, so cannot be removed. type=[{typeof(T)}]");
}
public bool Has()
{
- return TryGetTransient(typeof(T), out _) || TryGetScoped(typeof(T), out _) || TryGetSingleton(typeof(T), out _);
+ for (var i = 0 ; i < Descriptors.Count; i ++)
+ {
+ if (Descriptors[i].Interface == typeof(T))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
+ [Obsolete]
public bool TryGetTransient(Type type, out ServiceDescriptor descriptor)
{
- foreach (var serviceDescriptor in TransientServices)
+ foreach (var serviceDescriptor in Descriptors)
{
- if (serviceDescriptor.Interface == type)
+ if (serviceDescriptor.Interface == type && serviceDescriptor.Lifetime == DependencyLifetime.Transient)
{
descriptor = serviceDescriptor;
return true;
@@ -717,11 +745,13 @@ public bool TryGetTransient(Type type, out ServiceDescriptor descriptor)
descriptor = default(ServiceDescriptor);
return false;
}
+
+ [Obsolete]
public bool TryGetScoped(Type type, out ServiceDescriptor descriptor)
{
- foreach (var serviceDescriptor in ScopedServices)
+ foreach (var serviceDescriptor in Descriptors)
{
- if (serviceDescriptor.Interface == type)
+ if (serviceDescriptor.Interface == type && serviceDescriptor.Lifetime == DependencyLifetime.Scoped)
{
descriptor = serviceDescriptor;
return true;
@@ -732,11 +762,12 @@ public bool TryGetScoped(Type type, out ServiceDescriptor descriptor)
return false;
}
+ [Obsolete]
public bool TryGetSingleton(Type type, out ServiceDescriptor descriptor)
{
- foreach (var serviceDescriptor in SingletonServices)
+ foreach (var serviceDescriptor in Descriptors)
{
- if (serviceDescriptor.Interface == type)
+ if (serviceDescriptor.Interface == type && serviceDescriptor.Lifetime == DependencyLifetime.Singleton)
{
descriptor = serviceDescriptor;
return true;
@@ -752,9 +783,7 @@ public IDependencyBuilder Clone()
{
return new DependencyBuilder
{
- ScopedServices = new List(ScopedServices),
- TransientServices = new List(TransientServices),
- SingletonServices = new List(SingletonServices)
+ Descriptors = new List(Descriptors),
};
}
}
diff --git a/cli/beamable.common/Runtime/Dependencies/DependencyProvider.cs b/cli/beamable.common/Runtime/Dependencies/DependencyProvider.cs
index 13a539ede1..0f783803b4 100644
--- a/cli/beamable.common/Runtime/Dependencies/DependencyProvider.cs
+++ b/cli/beamable.common/Runtime/Dependencies/DependencyProvider.cs
@@ -165,14 +165,23 @@ public interface IDependencyProviderScope : IDependencyProvider
void RemoveChild(IDependencyProviderScope child);
}
- public class DependencyProvider : IDependencyProviderScope
+ public interface IDependencyOrderComparer : IComparer