Skip to content

Commit

Permalink
Remove unused code reduce allocations
Browse files Browse the repository at this point in the history
Removed code that is no longer used and reduced some allocations.
  • Loading branch information
smithgeek committed Nov 2, 2024
1 parent ee82337 commit 3193be8
Show file tree
Hide file tree
Showing 31 changed files with 897 additions and 1,530 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.100-rc.2.24474.11'
- name: Set VERSION variable from tag
run: echo "VERSION=${GITHUB_REF/refs\/tags\/v\//}" >> $GITHUB_ENV
- name: Build
Expand Down
40 changes: 22 additions & 18 deletions Benchmarks/Runner/Benchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Runner;

[MemoryDiagnoser, SimpleJob(launchCount: 1, warmupCount: 5, iterationCount: 50, invocationCount: 10000)]
[MemoryDiagnoser, SimpleJob(launchCount: 1, warmupCount: 10, iterationCount: 50, invocationCount: 10000)]
public class Benchmarks
{
const string QueryObjectParams = "?id=101&FirstName=Name&LastName=LastName&Age=23&phoneNumbers[0]=223422&phonenumbers[1]=11144" +
Expand Down Expand Up @@ -41,15 +41,17 @@ public class Benchmarks
Encoding.UTF8,
"application/json");

[Benchmark(Baseline = true)]
public async Task Voyager()
[Benchmark]
public Task AspNetCoreMvc()
{
await VoyagerClient.SendAsync(new()
var msg = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new($"{VoyagerClient.BaseAddress}benchmark/ok/123"),
RequestUri = new($"{MvcClient.BaseAddress}benchmark/ok/123"),
Content = _payload
});
};

return MvcClient.SendAsync(msg);
}

[Benchmark]
Expand All @@ -63,36 +65,38 @@ await VoyagerClient.SendAsync(new()
});
}

[Benchmark]
public Task MinimalApi()
[Benchmark(Baseline = true)]
public async Task Voyager()
{
var msg = new HttpRequestMessage
await VoyagerClient.SendAsync(new()
{
Method = HttpMethod.Post,
RequestUri = new($"{MinimalClient.BaseAddress}benchmark/ok/123"),
RequestUri = new($"{VoyagerClient.BaseAddress}benchmark/ok/123"),
Content = _payload
};
});
}

return MinimalClient.SendAsync(msg);

}

[Benchmark]
public Task AspNetCoreMvc()
public Task MinimalApi()
{
var msg = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new($"{MvcClient.BaseAddress}benchmark/ok/123"),
RequestUri = new($"{MinimalClient.BaseAddress}benchmark/ok/123"),
Content = _payload
};

return MvcClient.SendAsync(msg);
return MinimalClient.SendAsync(msg);

}



//[Benchmark]


[Benchmark]
public Task VoyagerTests()
{
var msg = new HttpRequestMessage
Expand Down Expand Up @@ -121,7 +125,7 @@ public Task FastEndpoints()



[Benchmark]
//[Benchmark]
public Task FastEndpointsCodeGen()
{
var msg = new HttpRequestMessage
Expand Down
44 changes: 44 additions & 0 deletions Benchmarks/Runner/SpeedTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma warning disable CA1822
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
using Voyager.Extensions;

namespace Runner;

[MemoryDiagnoser, SimpleJob(launchCount: 1, warmupCount: 10, iterationCount: 50, invocationCount: 300000)]
public class SpeedTests
{
private FluentValidation.Results.ValidationResult validationResult = new();

[GlobalSetup]
public void Setup()
{
validationResult.Errors.Add(new("test", "some message"));
validationResult.Errors.Add(new("UserId", "sdofinwe"));
validationResult.Errors.Add(new("awoien", "aowienf"));
}




[Benchmark(Baseline = true)]
public IResult OldWay()
{
var dictionary = validationResult.ToDictionary();
dictionary.ReplaceKey("UserId", "id");
return Results.ValidationProblem(dictionary);
}

[Benchmark]
public IResult TestWay()
{
return Results.ValidationProblem(validationResult.Errors.GroupBy(x =>
{
return x.PropertyName switch
{
"UserId" => "id",
_ => x.PropertyName
};
}).ToDictionary(g => g.Key, g => g.Select(x => x.ErrorMessage).ToArray()));
}
}
14 changes: 4 additions & 10 deletions Benchmarks/VoyagerBench/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public record Request(string LastName, string? FirstName = null)
public int UserId { get; init; }
public int? Age { get; set; }
public IEnumerable<string?>? PhoneNumbers { get; set; }
public required List<Complex> Complexes { get; set; }

public static void Validate(AbstractValidator<Request> validator)
{
Expand Down Expand Up @@ -114,19 +113,15 @@ public static void Configure(RouteHandlerBuilder routeHandlerBuilder)
.AllowAnonymous();
}

public IResult Post(Request req, ILogger<Program> logger)
public Response Post(Request req, ILogger<Program> logger)
{
if (req.Age > 13)
{
return TypedResults.Ok(new { Message = "You are too young." });
}
return TypedResults.Ok(new Response()
return new Response()
{
Id = req.UserId,
Name = req.FirstName + " " + req.LastName,
Age = req.Age ?? 0,
PhoneNumber = req.PhoneNumbers?.FirstOrDefault()
});
};
}
}

Expand All @@ -140,7 +135,7 @@ public static void Configure(RouteHandlerBuilder routeHandlerBuilder)
.AllowAnonymous();
}

public Response Post(ValidotRequest req, ILogger<Program> logger, Validot.Results.IValidationResult validationResult)
public Response Post(ValidotRequest req, ILogger<Program> logger)
{
return new Response()
{
Expand Down Expand Up @@ -226,7 +221,6 @@ public class Response
public string? Name { get; set; }
public int Age { get; set; }
public string? PhoneNumber { get; set; }
public List<Complex> Complexes { get; set; } = [];
}

public class Service
Expand Down
141 changes: 141 additions & 0 deletions src/Voyager.SourceGenerator/Endpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;

namespace Voyager.SourceGenerator;

internal class Endpoint
{
private const string IResultInterface = "Microsoft.AspNetCore.Http.IResult";

private readonly MethodDeclarationSyntax method;
private readonly SemanticModel semanticModel;
public RequestObject? Request { get; }

public bool NeedsAsync => IsTask || Request != null;
private readonly string[] requestNames = ["request", "req"];
public bool IsStatic => method.Modifiers.Any(SyntaxKind.StaticKeyword);
public IEnumerable<InstanceInfo> GetInjectedParameters()
{
return method.ParameterList.Parameters
.Select(p =>
{
if (requestNames.Any(rn => rn.Equals(p.Identifier.ValueText, StringComparison.Ordinal)))
{
return new InstanceInfo("request");
}
return semanticModel.GetTypeInfo(p.Type!).Type.GetInstanceOf();
}).Where(p => p != null)!;
}

public Endpoint(MethodDeclarationSyntax method, SemanticModel semanticModel, string httpMethod, string namePrefix, string path)
{
ReturnType = semanticModel.GetTypeInfo(method.ReturnType).Type;
if (ReturnType is INamedTypeSymbol namedSymbol &&
(ReturnType?.Name == "Task" || ReturnType?.Name == "ValueTask"))
{
IsTask = true;
ReturnType = namedSymbol.TypeArguments[0];
}
if (ReturnType?.ToDisplayString() == IResultInterface
|| (ReturnType?.AllInterfaces.Select(i => i.ToDisplayString()).Contains(IResultInterface) ?? false))
{
IsIResult = true;
}

this.method = method;
this.semanticModel = semanticModel;
HttpMethod = httpMethod;
Path = path.Trim('"');
NamePrefix = $"{namePrefix}{HttpMethod}";
var requestTypeSyntax = method.ParameterList.Parameters.FirstOrDefault(p => requestNames.Any(rn => rn.Equals(p.Identifier.Text, StringComparison.OrdinalIgnoreCase)))?.Type;
var requestType = string.Empty;
if (requestTypeSyntax is IdentifierNameSyntax name)
{
requestType = name.Identifier.ToFullString().Trim();
var requestTypeInfo = semanticModel.GetTypeInfo(name);
var declaringSyntax = semanticModel.GetSymbolInfo(name).Symbol?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax();
Request = new RequestObject(requestTypeInfo, NamePrefix, declaringSyntax, semanticModel);
}
}

public bool IsIResult { get; set; } = false;
public bool IsTask { get; set; } = false;
public ITypeSymbol? ReturnType { get; set; }
public string HttpMethod { get; }
public string Path { get; }
public string ResponseName => PathUtils.ToName(Path, "Response", HttpMethod);
public string NamePrefix { get; }

private IEnumerable<MethodResult> GetResultFromExpression(ExpressionSyntax? expression)
{
if (expression != null)
{
if (expression is ConditionalExpressionSyntax conditional)
{
return GetResultFromExpression(conditional.WhenTrue).Concat(
GetResultFromExpression(conditional.WhenFalse));
}
else if (expression is AwaitExpressionSyntax awaitSyntax)
{
return FindResultsInNodes(awaitSyntax.DescendantNodes(), true);
}
else if (expression is CastExpressionSyntax castExpression)
{
return GetResultFromExpression(castExpression.Expression);
}
else if (expression is ParenthesizedExpressionSyntax parenSyntax)
{
return GetResultFromExpression(parenSyntax.Expression);
}
var model = semanticModel.GetSymbolInfo(expression);
var result = MethodResult.Create(model.Symbol, semanticModel);
if (result != null)
{
return [result];
}
}
return [];
}

public List<MethodResult> FindResultsInNodes(IEnumerable<SyntaxNode> nodes, bool allowLambda)
{
List<MethodResult> results = [];
var returns = nodes.OfType<ReturnStatementSyntax>()
.Where(rs => allowLambda || !rs.AncestorsAndSelf().OfType<LambdaExpressionSyntax>().Any() &&
!rs.AncestorsAndSelf().OfType<LocalFunctionStatementSyntax>().Any())
.Where(n => n.IsKind(SyntaxKind.ReturnStatement)).ToList();
foreach (var statement in returns)
{
var expressionResults = GetResultFromExpression(statement.Expression);
results.AddRange(expressionResults);
}
return results;
}

public List<MethodResult> FindResults()
{
var results = new List<MethodResult>();

if (!IsIResult)
{
var result = MethodResult.Create(ReturnType, semanticModel);
if (result != null)
{
results.Add(result);
}
return results;
}

if (method.Body != null)
{
results.AddRange(FindResultsInNodes(method.Body.DescendantNodes(), false));
}
return results;
}
}
Loading

0 comments on commit 3193be8

Please sign in to comment.