From 3040e9c737e54a362777601f454793882b54fcda Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Wed, 5 Jun 2024 23:39:22 +0300 Subject: [PATCH] Parallelisation of metadata definition --- .../Core/ApiInvocationProcessor.cs | 22 ++--- src/Pure.DI.Core/Core/IMetadataVisitor.cs | 4 +- src/Pure.DI.Core/Core/InvocationVisitor.cs | 97 +++++++++++++++++++ src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs | 34 +++++-- src/Pure.DI.Core/Core/MetadataWalkerBase.cs | 6 +- src/Pure.DI.Core/Core/Models/MdHint.cs | 3 + src/Pure.DI.Core/Core/SetupsBuilder.cs | 10 +- 7 files changed, 148 insertions(+), 28 deletions(-) create mode 100644 src/Pure.DI.Core/Core/InvocationVisitor.cs create mode 100644 src/Pure.DI.Core/Core/Models/MdHint.cs diff --git a/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs b/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs index bdbdade06..b20b2a516 100644 --- a/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs +++ b/src/Pure.DI.Core/Core/ApiInvocationProcessor.cs @@ -10,7 +10,6 @@ internal class ApiInvocationProcessor( : IApiInvocationProcessor { private static readonly char[] TypeNamePartsSeparators = ['.']; - private readonly Hints _hints = new(); public void ProcessInvocation( IMetadataVisitor metadataVisitor, @@ -92,7 +91,7 @@ public void ProcessInvocation( var hintArgs = arguments.GetArgs(invocation.ArgumentList, "hint", "value"); if (hintArgs is [{ Expression: { } hintNameExpression }, { Expression: { } hintValueExpression }]) { - _hints[semanticModel.GetConstantValue(hintNameExpression)] = semanticModel.GetRequiredConstantValue(hintValueExpression); + metadataVisitor.VisitHint(new MdHint(semanticModel.GetConstantValue(hintNameExpression), semanticModel.GetRequiredConstantValue(hintValueExpression))); } break; @@ -116,6 +115,11 @@ public void ProcessInvocation( setupCompositionTypeName = baseType.Identifier.Text; } + foreach (var hint in comments.GetHints(invocationComments)) + { + metadataVisitor.VisitHint(new MdHint(hint.Key, hint.Value)); + } + metadataVisitor.VisitSetup( new MdSetup( semanticModel, @@ -123,7 +127,7 @@ public void ProcessInvocation( CreateCompositionName(setupCompositionTypeName, @namespace, invocation.ArgumentList), ImmutableArray.Empty, setupKind, - ApplyHints(invocationComments), + new Hints(), ImmutableArray.Empty, ImmutableArray.Empty, ImmutableArray.Empty, @@ -507,16 +511,6 @@ private static void VisitUsingDirectives( } } - private IHints ApplyHints(IEnumerable invocationComments) - { - foreach (var hint in comments.GetHints(invocationComments)) - { - _hints[hint.Key] = hint.Value; - } - - return _hints; - } - // ReSharper disable once SuggestBaseTypeForParameter private static void NotSupported(InvocationExpressionSyntax invocation) { @@ -539,7 +533,7 @@ private static ImmutableArray BuildTags(SemanticModel semanticModel, IEnu .Select((tag, i) => new MdTag(i, tag)) .ToImmutableArray(); } - + private static CompositionName CreateCompositionName(string name, string ns, SyntaxNode source) { string className; diff --git a/src/Pure.DI.Core/Core/IMetadataVisitor.cs b/src/Pure.DI.Core/Core/IMetadataVisitor.cs index b2f0be325..bc0d8d8f9 100644 --- a/src/Pure.DI.Core/Core/IMetadataVisitor.cs +++ b/src/Pure.DI.Core/Core/IMetadataVisitor.cs @@ -15,7 +15,7 @@ internal interface IMetadataVisitor void VisitFactory(in MdFactory factory); - void VisitResolve(MdResolver resolver); + void VisitResolve(in MdResolver resolver); void VisitDefaultLifetime(in MdDefaultLifetime defaultLifetime); @@ -36,6 +36,8 @@ internal interface IMetadataVisitor void VisitTag(in MdTag tag); void VisitAccumulator(in MdAccumulator accumulator); + + void VisitHint(in MdHint hint); void VisitFinish(); } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/InvocationVisitor.cs b/src/Pure.DI.Core/Core/InvocationVisitor.cs new file mode 100644 index 000000000..f079323f5 --- /dev/null +++ b/src/Pure.DI.Core/Core/InvocationVisitor.cs @@ -0,0 +1,97 @@ +namespace Pure.DI.Core; + +using System.Runtime.CompilerServices; + +internal readonly record struct InvocationVisitor( + SemanticModel SemanticModel, + InvocationExpressionSyntax Invocation, + IMetadataVisitor BaseVisitor, + CancellationToken CancellationToken) + : IMetadataVisitor +{ + private readonly List _actions = []; + + public void Apply() + { + foreach (var action in _actions) + { + CancellationToken.ThrowIfCancellationRequested(); + action.Run(); + } + } + + public void VisitSetup(in MdSetup setup) => + AddAction((visitor, i) => visitor.VisitSetup(i), BaseVisitor, setup); + + public void VisitUsingDirectives(in MdUsingDirectives usingDirectives) => + AddAction((visitor, i) => visitor.VisitUsingDirectives(i), BaseVisitor, usingDirectives); + + public void VisitBinding(in MdBinding binding) => + AddAction((visitor, i) => visitor.VisitBinding(i), BaseVisitor, binding); + + public void VisitContract(in MdContract contract) => + AddAction((visitor, i) => visitor.VisitContract(i), BaseVisitor, contract); + + public void VisitImplementation(in MdImplementation implementation) => + AddAction((visitor, i) => visitor.VisitImplementation(i), BaseVisitor, implementation); + + public void VisitFactory(in MdFactory factory) => + AddAction((visitor, i) => visitor.VisitFactory(i), BaseVisitor, factory); + + public void VisitResolve(in MdResolver resolver) => + AddAction((visitor, i) => visitor.VisitResolve(i), BaseVisitor, resolver); + + public void VisitDefaultLifetime(in MdDefaultLifetime defaultLifetime) => + AddAction((visitor, i) => visitor.VisitDefaultLifetime(i), BaseVisitor, defaultLifetime); + + public void VisitDependsOn(in MdDependsOn dependsOn) => + AddAction((visitor, i) => visitor.VisitDependsOn(i), BaseVisitor, dependsOn); + + public void VisitArg(in MdArg arg) => + AddAction((visitor, i) => visitor.VisitArg(i), BaseVisitor, arg); + + public void VisitRoot(in MdRoot root) => + AddAction((visitor, i) => visitor.VisitRoot(i), BaseVisitor, root); + + public void VisitTypeAttribute(in MdTypeAttribute typeAttribute) => + AddAction((visitor, i) => visitor.VisitTypeAttribute(i), BaseVisitor, typeAttribute); + + public void VisitTagAttribute(in MdTagAttribute tagAttribute) => + AddAction((visitor, i) => visitor.VisitTagAttribute(i), BaseVisitor, tagAttribute); + + public void VisitOrdinalAttribute(in MdOrdinalAttribute ordinalAttribute) => + AddAction((visitor, i) => visitor.VisitOrdinalAttribute(i), BaseVisitor, ordinalAttribute); + + public void VisitLifetime(in MdLifetime lifetime) => + AddAction((visitor, i) => visitor.VisitLifetime(i), BaseVisitor, lifetime); + + public void VisitTag(in MdTag tag) => + AddAction((visitor, i) => visitor.VisitTag(i), BaseVisitor, tag); + + public void VisitAccumulator(in MdAccumulator accumulator) => + AddAction((visitor, i) => visitor.VisitAccumulator(i), BaseVisitor, accumulator); + + public void VisitHint(in MdHint hint) => + AddAction((visitor, i) => visitor.VisitHint(i), BaseVisitor, hint); + + public void VisitFinish() => + AddAction((visitor, _) => visitor.VisitFinish(), BaseVisitor, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddAction(Action action, IMetadataVisitor visitor, in T state) => + _actions.Add(new VisitorAction(action, visitor, state)); + + private interface IRunnable + { + void Run(); + } + + private class VisitorAction( + Action action, + IMetadataVisitor visitor, + T state) + : IRunnable + { + public void Run() => action(visitor, state); + } +} \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs b/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs index a46ec5334..b13cf512f 100644 --- a/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs +++ b/src/Pure.DI.Core/Core/MetadataSyntaxWalker.cs @@ -23,10 +23,10 @@ internal sealed class MetadataSyntaxWalker( public void Visit(IMetadataVisitor metadataVisitor, in SyntaxUpdate update) { Visit(update.Node); - var invocations = new Stack(); + var visitors = new List(); while (_invocations.TryPop(out var invocation)) { - invocations.Push(invocation); + visitors.Add(new InvocationVisitor(update.SemanticModel, invocation, metadataVisitor, cancellationToken)); _isMetadata = true; try { @@ -38,14 +38,28 @@ public void Visit(IMetadataVisitor metadataVisitor, in SyntaxUpdate update) } } - while (invocations.TryPop(out var invocation)) + if (visitors.Count == 0) { - invocationProcessor.ProcessInvocation(metadataVisitor, update.SemanticModel, invocation, _namespace); + return; } - + + visitors.Reverse(); +#if DEBUG + visitors.ForEach(i => ProcessInvocation(i)); +#else + Parallel.ForEach(visitors, i => ProcessInvocation(i)); +#endif + foreach (var visitor in visitors) + { + visitor.Apply(); + } + metadataVisitor.VisitFinish(); } - + + private void ProcessInvocation(in InvocationVisitor visitor) => + invocationProcessor.ProcessInvocation(visitor, visitor.SemanticModel, visitor.Invocation, _namespace); + // ReSharper disable once CognitiveComplexity public override void VisitInvocationExpression(InvocationExpressionSyntax invocation) { @@ -67,7 +81,7 @@ public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax namesp _namespace = namespaceDeclaration.Name.ToString().Trim(); base.VisitNamespaceDeclaration(namespaceDeclaration); } - + private static string GetInvocationName(InvocationExpressionSyntax invocation) => GetName(invocation.Expression, 2); private static string GetName(ExpressionSyntax expression, int deepness = int.MaxValue) @@ -76,7 +90,7 @@ private static string GetName(ExpressionSyntax expression, int deepness = int.Ma { case IdentifierNameSyntax identifierNameSyntax: return identifierNameSyntax.Identifier.Text; - + case MemberAccessExpressionSyntax memberAccess when memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression): { var name = memberAccess.Name.Identifier.Text; @@ -88,12 +102,12 @@ private static string GetName(ExpressionSyntax expression, int deepness = int.Ma return name; } - + default: return string.Empty; } } - + [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] private static bool IsMetadata(InvocationExpressionSyntax invocation) => invocation diff --git a/src/Pure.DI.Core/Core/MetadataWalkerBase.cs b/src/Pure.DI.Core/Core/MetadataWalkerBase.cs index 95096840a..17c46f9d9 100644 --- a/src/Pure.DI.Core/Core/MetadataWalkerBase.cs +++ b/src/Pure.DI.Core/Core/MetadataWalkerBase.cs @@ -106,7 +106,7 @@ public virtual void VisitRoot(in MdRoot root) } } - public virtual void VisitResolve(MdResolver resolver) + public virtual void VisitResolve(in MdResolver resolver) { if (resolver.Tag is {} tag) { @@ -154,6 +154,10 @@ public virtual void VisitAccumulator(in MdAccumulator accumulator) { } + public virtual void VisitHint(in MdHint hint) + { + } + public virtual void VisitFinish() { } diff --git a/src/Pure.DI.Core/Core/Models/MdHint.cs b/src/Pure.DI.Core/Core/Models/MdHint.cs new file mode 100644 index 000000000..03608f8ae --- /dev/null +++ b/src/Pure.DI.Core/Core/Models/MdHint.cs @@ -0,0 +1,3 @@ +namespace Pure.DI.Core.Models; + +internal readonly record struct MdHint(Hint Key, string Value); \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/SetupsBuilder.cs b/src/Pure.DI.Core/Core/SetupsBuilder.cs index 077f35ad5..a4b836ef2 100644 --- a/src/Pure.DI.Core/Core/SetupsBuilder.cs +++ b/src/Pure.DI.Core/Core/SetupsBuilder.cs @@ -18,6 +18,7 @@ internal sealed class SetupsBuilder( private readonly List _accumulators = []; private IBindingBuilder _bindingBuilder = bindingBuilderFactory(); private MdSetup? _setup; + private Hints _hints = new(); public IEnumerable Build(SyntaxUpdate update) { @@ -69,7 +70,7 @@ public void VisitArg(in MdArg arg) FinishBinding(); } - public void VisitResolve(MdResolver resolver) + public void VisitResolve(in MdResolver resolver) { } @@ -98,6 +99,9 @@ public void VisitLifetime(in MdLifetime lifetime) => public void VisitAccumulator(in MdAccumulator accumulator) => _accumulators.Add(accumulator); + public void VisitHint(in MdHint hint) => + _hints[hint.Key] = hint.Value; + public void VisitFinish() => FinishSetup(); private void FinishBinding() => @@ -113,6 +117,7 @@ private void FinishSetup() _setups.Add( setup with { + Hints = _hints, Bindings = _bindings.Select(i => i with { SourceSetup = setup }).ToImmutableArray(), Roots = _roots.ToImmutableArray(), DependsOn = _dependsOn.ToImmutableArray(), @@ -122,7 +127,8 @@ setup with UsingDirectives = _usingDirectives.ToImmutableArray(), Accumulators = _accumulators.Distinct().ToImmutableArray() }); - + + _hints = new Hints(); _bindings.Clear(); _roots.Clear(); _dependsOn.Clear();