Skip to content

Commit f667a7b

Browse files
committed
perf(codegen): pool generated IInvokable request objects
1 parent 24b0259 commit f667a7b

37 files changed

Lines changed: 1234 additions & 138 deletions

File tree

src/Orleans.CodeGenerator/ActivatorGenerator.cs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private struct ConstructorArgument
1414
public TypeSyntax Type { get; set; }
1515
public string FieldName { get; set; }
1616
public string ParameterName { get; set; }
17+
public bool IsInvokablePool { get; set; }
1718
}
1819

1920
public ClassDeclarationSyntax GenerateActivator(ISerializableTypeDescription type)
@@ -28,7 +29,14 @@ public ClassDeclarationSyntax GenerateActivator(ISerializableTypeDescription typ
2829
{
2930
foreach (var arg in parameters)
3031
{
31-
orderedFields.Add(new ConstructorArgument { Type = arg, FieldName = $"_arg{index}", ParameterName = $"arg{index}" });
32+
orderedFields.Add(new ConstructorArgument
33+
{
34+
Type = arg,
35+
FieldName = $"_arg{index}",
36+
ParameterName = $"arg{index}",
37+
IsInvokablePool = type is GeneratedInvokableDescription { UsesInvokablePool: true }
38+
&& index == 0,
39+
});
3240
index++;
3341
}
3442
}
@@ -90,11 +98,14 @@ private static ConstructorDeclarationSyntax GenerateConstructor(
9098
{
9199
parameters.Add(Parameter(field.ParameterName.ToIdentifier()).WithType(field.Type));
92100

101+
var value = field.IsInvokablePool
102+
? field.ParameterName.ToIdentifierName()
103+
: Unwrapped(field.ParameterName.ToIdentifierName());
93104
body.Add(ExpressionStatement(
94-
AssignmentExpression(
95-
SyntaxKind.SimpleAssignmentExpression,
96-
field.FieldName.ToIdentifierName(),
97-
Unwrapped(field.ParameterName.ToIdentifierName()))));
105+
AssignmentExpression(
106+
SyntaxKind.SimpleAssignmentExpression,
107+
field.FieldName.ToIdentifierName(),
108+
value)));
98109
}
99110

100111
var constructorDeclaration = ConstructorDeclaration(simpleClassName)
@@ -114,6 +125,32 @@ static ExpressionSyntax Unwrapped(ExpressionSyntax expr)
114125

115126
private static MemberDeclarationSyntax GenerateCreateMethod(ISerializableTypeDescription type, List<ConstructorArgument> orderedFields)
116127
{
128+
foreach (var field in orderedFields)
129+
{
130+
if (field.IsInvokablePool)
131+
{
132+
var arguments = orderedFields.Select(static field => Argument(field.FieldName.ToIdentifierName()));
133+
var pooledCreateObject = ObjectCreationExpression(type.TypeSyntax)
134+
.WithArgumentList(ArgumentList(SeparatedList(arguments)));
135+
var tryGet = InvocationExpression(
136+
field.FieldName.ToIdentifierName().Member("TryGet"),
137+
ArgumentList(
138+
SingletonSeparatedList(
139+
Argument(
140+
DeclarationExpression(
141+
IdentifierName("var"),
142+
SingleVariableDesignation(Identifier("item"))))
143+
.WithRefKindKeyword(Token(SyntaxKind.OutKeyword)))));
144+
145+
return MethodDeclaration(type.TypeSyntax, "Create")
146+
.WithExpressionBody(
147+
ArrowExpressionClause(
148+
ConditionalExpression(tryGet, IdentifierName("item"), pooledCreateObject)))
149+
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
150+
.AddModifiers(Token(SyntaxKind.PublicKeyword));
151+
}
152+
}
153+
117154
ExpressionSyntax createObject;
118155
if (type.ActivatorConstructorParameters is { Count: > 0 })
119156
{
@@ -135,4 +172,5 @@ private static MemberDeclarationSyntax GenerateCreateMethod(ISerializableTypeDes
135172
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
136173
.AddModifiers(Token(SyntaxKind.PublicKeyword));
137174
}
175+
138176
}

src/Orleans.CodeGenerator/InvokableGenerator.cs

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,24 @@ public GeneratedInvokableDescription Generate(InvokableMethodDescription invokab
2222
var generatedClassName = GetSimpleClassName(invokableMethodInfo);
2323

2424
var baseClassType = GetBaseClassType(invokableMethodInfo);
25-
var fieldDescriptions = GetFieldDescriptions(invokableMethodInfo);
26-
var fields = GetFieldDeclarations(invokableMethodInfo, fieldDescriptions);
27-
var (ctor, ctorArgs) = GenerateConstructor(generatedClassName, invokableMethodInfo, baseClassType);
25+
var fieldDescriptions = GetFieldDescriptions(invokableMethodInfo, baseClassType);
26+
var invokableTypeSyntax = CreateInvokableTypeSyntax(generatedClassName, invokableMethodInfo);
27+
var fields = GetFieldDeclarations(invokableMethodInfo, fieldDescriptions, invokableTypeSyntax);
28+
var (ctor, ctorArgs) = GenerateConstructor(generatedClassName, invokableMethodInfo, baseClassType, fieldDescriptions, invokableTypeSyntax);
29+
var compatibilityCtor = fieldDescriptions.OfType<PoolFieldDescription>().Any()
30+
? ConstructorDeclaration(generatedClassName)
31+
.AddModifiers(Token(SyntaxKind.PublicKeyword))
32+
.WithInitializer(
33+
ConstructorInitializer(
34+
SyntaxKind.ThisConstructorInitializer,
35+
ArgumentList(
36+
SingletonSeparatedList(
37+
Argument(
38+
PostfixUnaryExpression(
39+
SyntaxKind.SuppressNullableWarningExpression,
40+
LiteralExpression(SyntaxKind.NullLiteralExpression)))))))
41+
.WithBody(Block())
42+
: null;
2843
var accessibility = GetAccessibility(method);
2944
var compoundTypeAliases = GetCompoundTypeAliasAttributeArguments(invokableMethodInfo, invokableMethodInfo.Key);
3045

@@ -52,6 +67,7 @@ public GeneratedInvokableDescription Generate(InvokableMethodDescription invokab
5267
baseClassType,
5368
fieldDescriptions,
5469
fields,
70+
compatibilityCtor,
5571
ctor,
5672
compoundTypeAliases,
5773
targetField,
@@ -77,6 +93,7 @@ [.. fieldDescriptions.OfType<IMemberDescription>()],
7793
serializationHooks,
7894
baseClassType,
7995
ctorArgs,
96+
fieldDescriptions.OfType<PoolFieldDescription>().Any(),
8097
compoundTypeAliases,
8198
returnValueInitializerMethod,
8299
classDeclaration);
@@ -106,6 +123,7 @@ private ClassDeclarationSyntax GetClassDeclarationSyntax(
106123
INamedTypeSymbol baseClassType,
107124
List<InvokerFieldDescription> fieldDescriptions,
108125
MemberDeclarationSyntax[] fields,
126+
ConstructorDeclarationSyntax? compatibilityCtor,
109127
ConstructorDeclarationSyntax? ctor,
110128
List<CompoundTypeAliasComponent[]> compoundTypeAliases,
111129
TargetFieldDescription targetField,
@@ -123,7 +141,12 @@ private ClassDeclarationSyntax GetClassDeclarationSyntax(
123141
AttributeList(SingletonSeparatedList(GetCompoundTypeAliasAttribute(alias))));
124142
}
125143

126-
if (ctor != null)
144+
if (compatibilityCtor is not null)
145+
{
146+
classDeclaration = classDeclaration.AddMembers(compatibilityCtor);
147+
}
148+
149+
if (ctor is not null)
127150
{
128151
classDeclaration = classDeclaration.AddMembers(ctor);
129152
}
@@ -568,6 +591,7 @@ private static MemberDeclarationSyntax GenerateDisposeMethod(
568591
INamedTypeSymbol baseClassType)
569592
{
570593
var body = new List<StatementSyntax>();
594+
PoolFieldDescription? poolField = null;
571595
foreach (var field in fields)
572596
{
573597
if (field is CancellationTokenSourceFieldDescription ctsField)
@@ -582,6 +606,11 @@ private static MemberDeclarationSyntax GenerateDisposeMethod(
582606
MemberBindingExpression(IdentifierName("Dispose"))))));
583607
}
584608

609+
if (field is PoolFieldDescription candidate)
610+
{
611+
poolField = candidate;
612+
}
613+
585614
if (field.IsInstanceField)
586615
{
587616
body.Add(
@@ -601,6 +630,17 @@ private static MemberDeclarationSyntax GenerateDisposeMethod(
601630
body.Add(ExpressionStatement(InvocationExpression(BaseExpression().Member("Dispose")).WithArgumentList(ArgumentList())));
602631
}
603632

633+
if (poolField is not null)
634+
{
635+
body.Add(
636+
ExpressionStatement(
637+
ConditionalAccessExpression(
638+
IdentifierName(poolField.FieldName),
639+
InvocationExpression(
640+
MemberBindingExpression(IdentifierName("Return")),
641+
ArgumentList(SingletonSeparatedList(Argument(ThisExpression())))))));
642+
}
643+
604644
return MethodDeclaration(PredefinedType(Token(SyntaxKind.VoidKeyword)), "Dispose")
605645
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)))
606646
.WithBody(Block(body));
@@ -678,9 +718,24 @@ public static string GetSimpleClassName(InvokableMethodDescription method)
678718
return $"Invokable_{method.ContainingInterface.Name}_{proxyKey}_{method.GeneratedMethodId}{typeArgs}";
679719
}
680720

721+
private static TypeSyntax CreateInvokableTypeSyntax(string generatedClassName, InvokableMethodDescription method)
722+
{
723+
if (method.AllTypeParameters.Count == 0)
724+
{
725+
return IdentifierName(generatedClassName);
726+
}
727+
728+
var typeArguments = method.AllTypeParameters.Select(parameter =>
729+
(TypeSyntax)IdentifierName(method.TypeParameterSubstitutions[parameter.Parameter]));
730+
return GenericName(
731+
Identifier(generatedClassName),
732+
TypeArgumentList(SeparatedList(typeArguments)));
733+
}
734+
681735
private MemberDeclarationSyntax[] GetFieldDeclarations(
682736
InvokableMethodDescription method,
683-
List<InvokerFieldDescription> fieldDescriptions)
737+
List<InvokerFieldDescription> fieldDescriptions,
738+
TypeSyntax invokableTypeSyntax)
684739
{
685740
return [.. fieldDescriptions.Select(GetFieldDeclaration)];
686741

@@ -708,6 +763,14 @@ MemberDeclarationSyntax GetFieldDeclaration(InvokerFieldDescription description)
708763
]))))))))
709764
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.ReadOnlyKeyword));
710765
}
766+
else if (description is PoolFieldDescription)
767+
{
768+
field = FieldDeclaration(
769+
VariableDeclaration(
770+
LibraryTypes.InvokablePool_1.ToTypeSyntax(invokableTypeSyntax),
771+
SingletonSeparatedList(VariableDeclarator(description.FieldName))))
772+
.AddModifiers(Token(SyntaxKind.PrivateKeyword), Token(SyntaxKind.ReadOnlyKeyword));
773+
}
711774
else
712775
{
713776
field = FieldDeclaration(
@@ -738,14 +801,30 @@ private static ExpressionSyntax GetTypesArray(InvokableMethodDescription method,
738801
private (ConstructorDeclarationSyntax? Constructor, List<TypeSyntax> ConstructorArguments) GenerateConstructor(
739802
string simpleClassName,
740803
InvokableMethodDescription method,
741-
INamedTypeSymbol baseClassType)
804+
INamedTypeSymbol baseClassType,
805+
List<InvokerFieldDescription> fieldDescriptions,
806+
TypeSyntax invokableTypeSyntax)
742807
{
743808
var parameters = new List<ParameterSyntax>();
744809

745810
var body = new List<StatementSyntax>();
746811

747812
List<TypeSyntax> constructorArgumentTypes = new();
748813
List<ArgumentSyntax> baseConstructorArguments = new();
814+
815+
if (fieldDescriptions.OfType<PoolFieldDescription>().FirstOrDefault() is { } poolField)
816+
{
817+
var poolType = LibraryTypes.InvokablePool_1.ToTypeSyntax(invokableTypeSyntax);
818+
constructorArgumentTypes.Add(poolType);
819+
parameters.Add(Parameter(Identifier("pool")).WithType(poolType));
820+
body.Add(
821+
ExpressionStatement(
822+
AssignmentExpression(
823+
SyntaxKind.SimpleAssignmentExpression,
824+
IdentifierName(poolField.FieldName),
825+
IdentifierName("pool"))));
826+
}
827+
749828
foreach (var constructor in baseClassType.GetAllMembers<IMethodSymbol>())
750829
{
751830
if (constructor.MethodKind != MethodKind.Constructor || constructor.DeclaredAccessibility == Accessibility.Private || constructor.IsImplicitlyDeclared)
@@ -791,7 +870,9 @@ private static ExpressionSyntax GetTypesArray(InvokableMethodDescription method,
791870
return (constructorDeclaration, constructorArgumentTypes);
792871
}
793872

794-
private List<InvokerFieldDescription> GetFieldDescriptions(InvokableMethodDescription method)
873+
private List<InvokerFieldDescription> GetFieldDescriptions(
874+
InvokableMethodDescription method,
875+
INamedTypeSymbol baseClassType)
795876
{
796877
var fields = new List<InvokerFieldDescription>();
797878
uint fieldId = 0;
@@ -811,7 +892,27 @@ private List<InvokerFieldDescription> GetFieldDescriptions(InvokableMethodDescri
811892
fields.Add(new CancellationTokenSourceFieldDescription(LibraryTypes));
812893
}
813894

895+
var requiresDependencyInjection = baseClassType.GetAllMembers<IMethodSymbol>()
896+
.Any(constructor =>
897+
constructor.MethodKind == MethodKind.Constructor
898+
&& constructor.HasAttribute(LibraryTypes.GeneratedActivatorConstructorAttribute));
899+
if (method.MethodTypeParameters.Count == 0
900+
&& method.CustomInitializerMethods.Count == 0
901+
&& !requiresDependencyInjection
902+
&& IsPoolableBaseType(baseClassType))
903+
{
904+
fields.Add(new PoolFieldDescription(LibraryTypes));
905+
}
906+
814907
return fields;
908+
909+
static bool IsPoolableBaseType(INamedTypeSymbol type)
910+
=> type.ContainingNamespace.ToDisplayString() == "Orleans.Runtime"
911+
&& type.MetadataName is "Request"
912+
or "Request`1"
913+
or "TaskRequest"
914+
or "TaskRequest`1"
915+
or "VoidRequest";
815916
}
816917

817918
internal abstract class InvokerFieldDescription(ITypeSymbol fieldType, string fieldName)
@@ -910,4 +1011,10 @@ internal sealed class MethodInfoFieldDescription(ITypeSymbol fieldType, string f
9101011
public override bool IsSerializable => false;
9111012
public override bool IsInstanceField => false;
9121013
}
1014+
1015+
internal sealed class PoolFieldDescription(LibraryTypes libraryTypes) : InvokerFieldDescription(libraryTypes.InvokablePool_1, "_pool")
1016+
{
1017+
public override bool IsSerializable => false;
1018+
public override bool IsInstanceField => false;
1019+
}
9131020
}

src/Orleans.CodeGenerator/LibraryTypes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ private LibraryTypes(Compilation compilation, CodeGeneratorOptions options)
3131
GenerateSerializerAttribute = Type("Orleans.GenerateSerializerAttribute");
3232
SerializationCallbacksAttribute = Type("Orleans.SerializationCallbacksAttribute");
3333
IActivator_1 = Type("Orleans.Serialization.Activators.IActivator`1");
34+
InvokablePool_1 = Type("Orleans.Serialization.Invocation.InvokablePool`1");
3435
IBufferWriter = Type("System.Buffers.IBufferWriter`1");
3536
IdAttributeType = Type(CodeGeneratorOptions.IdAttribute);
3637
ConstructorAttributeTypes = [.. CodeGeneratorOptions.ConstructorAttributes.Select(Type)];
@@ -213,6 +214,7 @@ INamedTypeSymbol Type(string metadataName)
213214
public INamedTypeSymbol GenerateMethodSerializersAttribute { get; private set; }
214215
public INamedTypeSymbol GenerateSerializerAttribute { get; private set; }
215216
public INamedTypeSymbol IActivator_1 { get; private set; }
217+
public INamedTypeSymbol InvokablePool_1 { get; private set; }
216218
public INamedTypeSymbol IBufferWriter { get; private set; }
217219
public INamedTypeSymbol IInvokable { get; private set; }
218220
public INamedTypeSymbol ITargetHolder { get; private set; }

src/Orleans.CodeGenerator/Model/GeneratedInvokableDescription.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public GeneratedInvokableDescription(
1818
List<INamedTypeSymbol> serializationHooks,
1919
INamedTypeSymbol baseType,
2020
List<TypeSyntax> constructorArguments,
21+
bool usesInvokablePool,
2122
List<CompoundTypeAliasComponent[]> compoundTypeAliases,
2223
string? returnValueInitializerMethod,
2324
ClassDeclarationSyntax classDeclarationSyntax)
@@ -39,6 +40,7 @@ public GeneratedInvokableDescription(
3940
Accessibility = accessibility;
4041
SerializationHooks = serializationHooks;
4142
ActivatorConstructorParameters = constructorArguments;
43+
UsesInvokablePool = usesInvokablePool;
4244
CompoundTypeAliases = compoundTypeAliases;
4345
ReturnValueInitializerMethod = returnValueInitializerMethod;
4446
ClassDeclarationSyntax = classDeclarationSyntax;
@@ -72,6 +74,7 @@ public GeneratedInvokableDescription(
7274
public bool IsImmutable => false;
7375
public bool IsExceptionType => false;
7476
public List<TypeSyntax> ActivatorConstructorParameters { get; }
77+
public bool UsesInvokablePool { get; }
7578
public bool HasActivatorConstructor => UseActivator;
7679
public List<CompoundTypeAliasComponent[]> CompoundTypeAliases { get; }
7780
public ClassDeclarationSyntax ClassDeclarationSyntax { get; }

src/Orleans.CodeGenerator/Model/ProxyMethodDescription.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public ConstructedGeneratedInvokableDescription(GeneratedInvokableDescription in
148148
public bool IsImmutable => _invokableDescription.IsImmutable;
149149
public bool IsExceptionType => _invokableDescription.IsExceptionType;
150150
public List<TypeSyntax> ActivatorConstructorParameters => _invokableDescription.ActivatorConstructorParameters;
151+
public bool UsesInvokablePool => _invokableDescription.UsesInvokablePool;
151152
public bool HasActivatorConstructor => UseActivator;
152153
public string? ReturnValueInitializerMethod => _invokableDescription.ReturnValueInitializerMethod;
153154

0 commit comments

Comments
 (0)