From: AeonLucid Date: Mon, 26 Oct 2020 23:40:10 +0000 (+0100) Subject: Remove EventManager CallAsync memory allocation X-Git-Tag: v1.2.2~63 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=dcc4da25de92ed9cc885e6bcb3ad105a1f55f902;p=rhonda%2Fimpostor.git Remove EventManager CallAsync memory allocation --- diff --git a/src/Impostor.Benchmarks/.gitignore b/src/Impostor.Benchmarks/.gitignore new file mode 100644 index 0000000..1c2dac6 --- /dev/null +++ b/src/Impostor.Benchmarks/.gitignore @@ -0,0 +1 @@ +BenchmarkDotNet.Artifacts \ No newline at end of file diff --git a/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj b/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj new file mode 100644 index 0000000..697de52 --- /dev/null +++ b/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + + + + + + + + + + + diff --git a/src/Impostor.Benchmarks/Program.cs b/src/Impostor.Benchmarks/Program.cs new file mode 100644 index 0000000..213070f --- /dev/null +++ b/src/Impostor.Benchmarks/Program.cs @@ -0,0 +1,18 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Running; +using Impostor.Benchmarks.Tests; + +namespace Impostor.Benchmarks +{ + internal static class Program + { + private static void Main(string[] args) + { + BenchmarkRunner.Run( + DefaultConfig.Instance + .AddDiagnoser(MemoryDiagnoser.Default) + ); + } + } +} diff --git a/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs b/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs new file mode 100644 index 0000000..4470d92 --- /dev/null +++ b/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs @@ -0,0 +1,77 @@ +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Impostor.Api.Events; +using Impostor.Api.Events.Managers; +using Impostor.Server.Events; +using Microsoft.Extensions.DependencyInjection; + +namespace Impostor.Benchmarks.Tests +{ + public class EventManagerBenchmark + { + private IEventManager _eventManager; + private IGameEvent _event; + + [GlobalSetup] + public void Setup() + { + var services = new ServiceCollection(); + + services.AddLogging(); + services.AddSingleton(); + + _event = new GameStartedEvent(null); + _eventManager = services.BuildServiceProvider().GetRequiredService(); + _eventManager.RegisterListener(new EventListener()); + _eventManager.RegisterListener(new EventListener()); + _eventManager.RegisterListener(new EventListener()); + _eventManager.RegisterListener(new EventListener()); + _eventManager.RegisterListener(new EventListener()); + } + + [Benchmark] + public async Task Run_1() + { + for (var i = 0; i < 1; i++) + { + await _eventManager.CallAsync(_event); + } + } + + [Benchmark] + public async Task Run_1000() + { + for (var i = 0; i < 1000; i++) + { + await _eventManager.CallAsync(_event); + } + } + + [Benchmark] + public async Task Run_10000() + { + for (var i = 0; i < 10000; i++) + { + await _eventManager.CallAsync(_event); + } + } + + [Benchmark] + public async Task Run_100000() + { + for (var i = 0; i < 100000; i++) + { + await _eventManager.CallAsync(_event); + } + } + + private class EventListener : IEventListener + { + [EventListener] + public void OnGameStarted(IGameStartedEvent e) + { + + } + } + } +} diff --git a/src/Impostor.Server/Events/EventManager.cs b/src/Impostor.Server/Events/EventManager.cs index 10c3ec8..df8829f 100644 --- a/src/Impostor.Server/Events/EventManager.cs +++ b/src/Impostor.Server/Events/EventManager.cs @@ -1,8 +1,7 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Threading.Tasks; using Impostor.Api; using Impostor.Api.Events; @@ -16,6 +15,7 @@ namespace Impostor.Server.Events internal class EventManager : IEventManager { private readonly ConcurrentDictionary _temporaryEventListeners; + private readonly ConcurrentDictionary> _cachedEventHandlers; private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; @@ -24,6 +24,7 @@ namespace Impostor.Server.Events _logger = logger; _serviceProvider = serviceProvider; _temporaryEventListeners = new ConcurrentDictionary(); + _cachedEventHandlers = new ConcurrentDictionary>(); } /// @@ -54,6 +55,11 @@ namespace Impostor.Server.Events register.Add(wrappedEventListener); } + if (eventListeners.Count > 0) + { + _cachedEventHandlers.TryRemove(typeof(TListener), out _); + } + return new MultiDisposable(disposes); } @@ -61,7 +67,12 @@ namespace Impostor.Server.Events public bool IsRegistered() where TEvent : IEvent { - return GetHandlers(_serviceProvider).Any(); + if (_cachedEventHandlers.TryGetValue(typeof(TEvent), out var handlers)) + { + return handlers.Count > 0; + } + + return GetHandlers().Any(); } /// @@ -70,8 +81,12 @@ namespace Impostor.Server.Events { try { - foreach (var (handler, eventListener) in GetHandlers(_serviceProvider) - .OrderByDescending(e => e.Listener.Priority)) + if (!_cachedEventHandlers.TryGetValue(typeof(T), out var handlers)) + { + handlers = CacheEventHandlers(); + } + + foreach (var (handler, eventListener) in handlers) { await eventListener.InvokeAsync(handler, @event, _serviceProvider); } @@ -86,12 +101,23 @@ namespace Impostor.Server.Events } } + private List CacheEventHandlers() + where TEvent : IEvent + { + var handlers = GetHandlers() + .OrderByDescending(e => e.Listener.Priority) + .ToList(); + + _cachedEventHandlers[typeof(TEvent)] = handlers; + + return handlers; + } + /// /// Get all the event listeners for the given event type. /// - /// Current service provider. /// The event listeners. - private IEnumerable GetHandlers(IServiceProvider services) + private IEnumerable GetHandlers() where TEvent : IEvent { var eventType = typeof(TEvent); @@ -108,7 +134,7 @@ namespace Impostor.Server.Events } } - foreach (var handler in services.GetServices()) + foreach (var handler in _serviceProvider.GetServices()) { if (handler is IManualEventListener manualEventListener && manualEventListener.CanExecute()) { diff --git a/src/Impostor.Server/Properties/AssemblyInfo.cs b/src/Impostor.Server/Properties/AssemblyInfo.cs index 99d6f86..84c5158 100644 --- a/src/Impostor.Server/Properties/AssemblyInfo.cs +++ b/src/Impostor.Server/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +[assembly:InternalsVisibleTo("Impostor.Benchmarks")] [assembly:InternalsVisibleTo("Impostor.Tests")] -[assembly:InternalsVisibleTo("Impostor.Tools.ServerReplay")] \ No newline at end of file +[assembly:InternalsVisibleTo("Impostor.Tools.ServerReplay")] diff --git a/src/Impostor.sln b/src/Impostor.sln index 9ec2b61..b9706de 100644 --- a/src/Impostor.sln +++ b/src/Impostor.sln @@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Tools.ServerReplay EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Plugins.Example", "Impostor.Plugins.Example\Impostor.Plugins.Example.csproj", "{70F97BB7-12A1-4C7A-B2A5-B962D14B813E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Benchmarks", "Impostor.Benchmarks\Impostor.Benchmarks.csproj", "{EA04E386-6CCB-4C52-8C82-64F32C7EB377}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -146,6 +148,14 @@ Global {70F97BB7-12A1-4C7A-B2A5-B962D14B813E}.Release|Any CPU.Build.0 = Release|Any CPU {70F97BB7-12A1-4C7A-B2A5-B962D14B813E}.Release|x86.ActiveCfg = Release|Any CPU {70F97BB7-12A1-4C7A-B2A5-B962D14B813E}.Release|x86.Build.0 = Release|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Debug|x86.Build.0 = Debug|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Release|Any CPU.Build.0 = Release|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Release|x86.ActiveCfg = Release|Any CPU + {EA04E386-6CCB-4C52-8C82-64F32C7EB377}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE