Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions benchmarks/Mediator.Benchmarks/Messaging/RequestUnitBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using Microsoft.Extensions.DependencyInjection;

namespace Mediator.Benchmarks.Messaging;

public sealed record RequestWithoutResponse : IRequest, MediatR.IRequest;

public sealed record RequestAsyncWithoutResponse : IRequest, MediatR.IRequest;

public sealed class RequestWithoutResponseHandler
: IRequestHandler<RequestWithoutResponse>,
MediatR.IRequestHandler<RequestWithoutResponse>
{
public ValueTask Handle(RequestWithoutResponse request, CancellationToken cancellationToken) =>
ValueTask.CompletedTask;

Task MediatR.IRequestHandler<RequestWithoutResponse>.Handle(
RequestWithoutResponse request,
CancellationToken cancellationToken
) => Task.CompletedTask;
}

public sealed class RequestAsyncWithoutResponseHandler
: IRequestHandler<RequestAsyncWithoutResponse>,
MediatR.IRequestHandler<RequestAsyncWithoutResponse>
{
public async ValueTask Handle(RequestAsyncWithoutResponse request, CancellationToken cancellationToken) =>
await ValueTask.CompletedTask;

async Task MediatR.IRequestHandler<RequestAsyncWithoutResponse>.Handle(
RequestAsyncWithoutResponse request,
CancellationToken cancellationToken
) => await Task.CompletedTask;
}

[ConfigSource]
public class RequestWithoutResponseBenchmarks
{
private sealed class ConfigSourceAttribute : Attribute, IConfigSource
{
public IConfig Config { get; }

public ConfigSourceAttribute()
{
var lifetimes = Enum.GetValues<ServiceLifetime>();
bool[] largeProjectOptions = [false, true];
var jobs =
from lifetime in lifetimes
from largeProject in largeProjectOptions
select Job
.Default.WithArguments(
[
new MsBuildArgument(
$"/p:ExtraDefineConstants=Mediator_Lifetime_{lifetime}"
+ (largeProject ? $"%3BMediator_Large_Project" : "")
),
]
)
.WithEnvironmentVariable("ServiceLifetime", lifetime.ToString())
.WithEnvironmentVariable("IsLargeProject", $"{largeProject}")
.WithCustomBuildConfiguration($"{lifetime}/{largeProject}")
.WithId($"{lifetime}/{largeProject}");

Config = ManualConfig
.CreateEmpty()
.AddJob(jobs.ToArray())
.AddColumn(new CustomColumn("ServiceLifetime", (_, c) => c.Job.Id.Split('/')[0]))
.AddColumn(
new CustomColumn("Project type", (_, c) => c.Job.Id.Split('/')[1] == "True" ? "Large" : "Small")
)
.WithOption(ConfigOptions.KeepBenchmarkFiles, false)
.HideColumns(Column.Arguments, Column.EnvironmentVariables, Column.BuildConfiguration, Column.Job)
.WithSummaryStyle(SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend))
.AddColumn(RankColumn.Arabic)
.WithOrderer(new DefaultOrderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared))
.AddDiagnoser(MemoryDiagnoser.Default);
}
}

private IServiceProvider _serviceProvider;
private IServiceScope _serviceScope;
private IMediator _mediator;
private Mediator _concreteMediator;
private MediatR.IMediator _mediatr;
private RequestWithoutResponseHandler _handler;
private RequestWithoutResponse _request;
private RequestAsyncWithoutResponseHandler _handlerAsync;
private RequestAsyncWithoutResponse _requestAsync;

[GlobalSetup]
public void Setup()
{
Fixture.Setup();

var services = new ServiceCollection();
services.AddMediator();
services.AddMediatR(opts =>
{
opts.Lifetime = Mediator.ServiceLifetime;
opts.RegisterServicesFromAssembly(typeof(RequestHandler).Assembly);
});

_serviceProvider = services.BuildServiceProvider();
#pragma warning disable CS0162 // Unreachable code detected
if (Mediator.ServiceLifetime == ServiceLifetime.Scoped)
{
_serviceScope = _serviceProvider.CreateScope();
_serviceProvider = _serviceScope.ServiceProvider;
}
#pragma warning restore CS0162 // Unreachable code detected

_mediator = _serviceProvider.GetRequiredService<IMediator>();
_concreteMediator = _serviceProvider.GetRequiredService<Mediator>();
_mediatr = _serviceProvider.GetRequiredService<MediatR.IMediator>();
_handler = _serviceProvider.GetRequiredService<RequestWithoutResponseHandler>();
_handlerAsync = _serviceProvider.GetRequiredService<RequestAsyncWithoutResponseHandler>();
_request = new();
_requestAsync = new();
}

[GlobalCleanup]
public void Cleanup()
{
if (_serviceScope is not null)
_serviceScope.Dispose();
else
(_serviceProvider as IDisposable)?.Dispose();
}

[Benchmark]
public Task SendRequest_MediatR()
{
return _mediatr.Send(_request, CancellationToken.None);
}

[Benchmark]
public ValueTask SendRequest_IMediator()
{
return _mediator.Send(_request, CancellationToken.None);
}

[Benchmark]
public ValueTask SendRequest_Mediator()
{
return _concreteMediator.Send(_request, CancellationToken.None);
}

[Benchmark(Baseline = true)]
public ValueTask SendRequest_Baseline()
{
return _handler.Handle(_request, CancellationToken.None);
}

[Benchmark]
public Task SendRequest_Async_MediatR()
{
return _mediatr.Send(_requestAsync, CancellationToken.None);
}

[Benchmark]
public ValueTask SendRequest_Async_IMediator()
{
return _mediator.Send(_requestAsync, CancellationToken.None);
}

[Benchmark]
public ValueTask SendRequest_Async_Mediator()
{
return _concreteMediator.Send(_requestAsync, CancellationToken.None);
}

[Benchmark]
public ValueTask SendRequest_Async_Baseline()
{
return _handlerAsync.Handle(_requestAsync, CancellationToken.None);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"MassTransitIntegration": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:50391;http://localhost:50392"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ public void Initialize()
RequestMessageHandlerWrappers = new RequestMessageHandlerWrapperModel[]
{
new RequestMessageHandlerWrapperModel("Request", this),
new RequestMessageHandlerWrapperModel("Request", this, false),
new RequestMessageHandlerWrapperModel("StreamRequest", this),
new RequestMessageHandlerWrapperModel("Command", this),
new RequestMessageHandlerWrapperModel("Command", this, false),
new RequestMessageHandlerWrapperModel("StreamCommand", this),
new RequestMessageHandlerWrapperModel("Query", this),
new RequestMessageHandlerWrapperModel("StreamQuery", this),
Expand Down Expand Up @@ -619,7 +621,7 @@ bool ProcessInterface(
typeInterfaceSymbol.Name.IndexOf("Handler") - 1
);

var handler = new RequestMessageHandler(typeSymbol, messageType, this);
var handler = new RequestMessageHandler(typeSymbol, typeInterfaceSymbol, messageType, this);
var requestMessageSymbol = (INamedTypeSymbol)typeInterfaceSymbol.TypeArguments[0];
if (mapping.TryGetValue(requestMessageSymbol, out var requestMessageObj))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ CompilationAnalyzer analyzer
)
: base(symbol, analyzer)
{
bool hasResponse = !responseSymbol.Name.Equals("Unit", StringComparison.Ordinal);
ResponseSymbol = responseSymbol;
WrapperType = analyzer.RequestMessageHandlerWrappers.Single(w => w.MessageType == messageType);
WrapperType = analyzer.RequestMessageHandlerWrappers.Single(w =>
w.MessageType == messageType && w.HasResponse == hasResponse
);
MessageType = messageType;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
using System.Threading.Tasks.Sources;

namespace Mediator.SourceGenerator;

internal sealed class RequestMessageHandler : MessageHandler<RequestMessageHandler>
{
private readonly string _messageType;
private readonly RequestMessageHandlerWrapperModel _wrapperType;

public RequestMessageHandler(INamedTypeSymbol symbol, string messageType, CompilationAnalyzer analyzer)
public RequestMessageHandler(
INamedTypeSymbol symbol,
INamedTypeSymbol interfaceSymbol,
string messageType,
CompilationAnalyzer analyzer
)
: base(symbol, analyzer)
{
var hasResponse = interfaceSymbol.TypeArguments.Length == 2;
_messageType = messageType;
_wrapperType = analyzer.RequestMessageHandlerWrappers.Single(w => w.MessageType == messageType);
_wrapperType = analyzer.RequestMessageHandlerWrappers.Single(w =>
w.MessageType == messageType && w.HasResponse == hasResponse
);
}

public RequestMessageHandlerModel ToModel()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Mediator.SourceGenerator;
namespace Mediator.SourceGenerator;

internal sealed record CompilationModel
{
Expand Down Expand Up @@ -31,6 +31,8 @@ public CompilationModel(string mediatorNamespace, string generatorVersion)

IRequestMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
ICommandMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
IUnitRequestMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
IUnitCommandMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
IQueryMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
IStreamRequestMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
IStreamQueryMessages = ImmutableEquatableArray<RequestMessageModel>.Empty;
Expand Down Expand Up @@ -81,6 +83,8 @@ bool generateTypesAsInternal

var iRequestMessages = new List<RequestMessageModel>();
var iCommandMessages = new List<RequestMessageModel>();
var iUnitRequestMessages = new List<RequestMessageModel>();
var iUnitCommandMessages = new List<RequestMessageModel>();
var iQueryMessages = new List<RequestMessageModel>();
var iStreamRequestMessages = new List<RequestMessageModel>();
var iStreamQueryMessages = new List<RequestMessageModel>();
Expand All @@ -94,8 +98,10 @@ bool generateTypesAsInternal
reqMessages.Add(r);
var list = r.MessageKind switch
{
RequestMessageKind.Request => iRequestMessages,
RequestMessageKind.Command => iCommandMessages,
RequestMessageKind.Request when r.HasResponse => iRequestMessages,
RequestMessageKind.Command when r.HasResponse => iCommandMessages,
RequestMessageKind.Request when !r.HasResponse => iUnitRequestMessages,
RequestMessageKind.Command when !r.HasResponse => iUnitCommandMessages,
RequestMessageKind.Query => iQueryMessages,
RequestMessageKind.StreamRequest => iStreamRequestMessages,
RequestMessageKind.StreamQuery => iStreamQueryMessages,
Expand All @@ -118,6 +124,8 @@ or RequestMessageKind.StreamQuery

IRequestMessages = new(iRequestMessages);
ICommandMessages = new(iCommandMessages);
IUnitCommandMessages = new(iUnitCommandMessages);
IUnitRequestMessages = new(iUnitRequestMessages);
IQueryMessages = new(iQueryMessages);
IStreamRequestMessages = new(iStreamRequestMessages);
IStreamQueryMessages = new(iStreamQueryMessages);
Expand All @@ -126,20 +134,24 @@ or RequestMessageKind.StreamQuery
HasRequests = iRequestMessages.Count > 0;
HasCommands = iCommandMessages.Count > 0;
HasQueries = iQueryMessages.Count > 0;
HasUnitRequests = iUnitRequestMessages.Count > 0;
HasUnitCommands = iUnitCommandMessages.Count > 0;
HasStreamRequests = iStreamRequestMessages.Count > 0;
HasStreamQueries = iStreamQueryMessages.Count > 0;
HasStreamCommands = iStreamCommandMessages.Count > 0;
HasNotifications = notificationMessages.Count > 0;

HasManyRequests = iRequestMessages.Count > ManyMessagesTreshold;
HasManyUnitRequests = iUnitRequestMessages.Count > ManyMessagesTreshold;
HasManyCommands = iCommandMessages.Count > ManyMessagesTreshold;
HasManyUnitCommands = iUnitCommandMessages.Count > ManyMessagesTreshold;
HasManyQueries = iQueryMessages.Count > ManyMessagesTreshold;
HasManyStreamRequests = iStreamRequestMessages.Count > ManyMessagesTreshold;
HasManyStreamQueries = iStreamQueryMessages.Count > ManyMessagesTreshold;
HasManyStreamCommands = iStreamCommandMessages.Count > ManyMessagesTreshold;
HasManyNotifications = notificationMessages.Count > ManyMessagesTreshold;

HasAnyRequest = HasRequests || HasCommands || HasQueries;
HasAnyRequest = HasRequests || HasCommands || HasQueries || HasUnitCommands || HasUnitRequests;
HasAnyStreamRequest = HasStreamRequests || HasStreamQueries || HasStreamCommands;
}

Expand Down Expand Up @@ -173,22 +185,28 @@ or RequestMessageKind.StreamQuery

public ImmutableEquatableArray<RequestMessageModel> IRequestMessages { get; }
public ImmutableEquatableArray<RequestMessageModel> ICommandMessages { get; }
public ImmutableEquatableArray<RequestMessageModel> IUnitRequestMessages { get; }
public ImmutableEquatableArray<RequestMessageModel> IUnitCommandMessages { get; }
public ImmutableEquatableArray<RequestMessageModel> IQueryMessages { get; }

public ImmutableEquatableArray<RequestMessageModel> IStreamRequestMessages { get; }
public ImmutableEquatableArray<RequestMessageModel> IStreamQueryMessages { get; }
public ImmutableEquatableArray<RequestMessageModel> IStreamCommandMessages { get; }

public bool HasRequests { get; }
public bool HasUnitRequests { get; }
public bool HasCommands { get; }
public bool HasUnitCommands { get; }
public bool HasQueries { get; }
public bool HasStreamRequests { get; }
public bool HasStreamQueries { get; }
public bool HasStreamCommands { get; }
public bool HasNotifications { get; }

public bool HasManyRequests { get; }
public bool HasManyUnitRequests { get; }
public bool HasManyCommands { get; }
public bool HasManyUnitCommands { get; }
public bool HasManyQueries { get; }
public bool HasManyStreamRequests { get; }
public bool HasManyStreamQueries { get; }
Expand Down
Loading