Skip to content

Commit 32d743d

Browse files
Reduce intermediate array allocations in inline hint implementation (#81309)
AbstractInlineHintService currently retrieves two intermediate arrays from the type and parameter services and then joins them together and returns the resultant array. Instead, pass an ArrayBuilder to the type and parameter services and have them populate it directly, and then only allocate a single IA from the ArrayBuilder. Test insertion: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/689100 This previously showed as 4.5% (5.0 MB) of RoslynCodeAnalysisService allocations in the html completion scenario in the RazorEditingTests.CompletionInCohosting speedometer test. With this change, allocations of the InlineHint[] went down to 1.8% (2.1 MB) *** BEFORE *** <img width="1307" height="351" alt="image" src="https://github.com/user-attachments/assets/1c060089-c103-417b-8c60-7bf3641bc5a5" /> *** AFTER (roslyn symbols didn't load for some reason) *** <img width="1301" height="331" alt="image" src="https://github.com/user-attachments/assets/cbe4ecc7-38a7-4dcd-a7ff-9e83f231fee7" /> --------- Co-authored-by: Cyrus Najmabadi <cyrus.najmabadi@gmail.com>
1 parent 6e773e2 commit 32d743d

File tree

6 files changed

+39
-31
lines changed

6 files changed

+39
-31
lines changed

src/EditorFeatures/Test2/InlineHints/AbstractInlineHintsTests.vb

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Imports System.Collections.Immutable
66
Imports System.Threading
77
Imports Microsoft.CodeAnalysis.InlineHints
88
Imports Microsoft.CodeAnalysis.LanguageService
9+
Imports Microsoft.CodeAnalysis.PooledObjects
910
Imports Microsoft.CodeAnalysis.Text
1011

1112
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InlineHints
@@ -28,8 +29,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InlineHints
2829
Dim tagService = document.GetRequiredLanguageService(Of IInlineParameterNameHintsService)
2930

3031
Dim span = If(hostDocument.SelectedSpans.Any(), hostDocument.SelectedSpans.Single(), New TextSpan(0, snapshot.Length))
31-
Dim inlineHints = Await tagService.GetInlineHintsAsync(
32-
document, span, options, displayOptions, displayAllOverride:=False, CancellationToken.None)
32+
Dim inlineHints = ArrayBuilder(Of InlineHint).GetInstance()
33+
34+
Await tagService.AddInlineHintsAsync(
35+
document, span, options, displayOptions, displayAllOverride:=False, inlineHints, CancellationToken.None)
3336

3437
Dim producedTags = From hint In inlineHints
3538
Select hint.DisplayParts.GetFullText().TrimEnd() + hint.Span.ToString
@@ -56,7 +59,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InlineHints
5659
AssertEx.Equal(expectedTags, producedTags)
5760
End Sub
5861

59-
Private Shared Async Function ValidateDoubleClick(document As Document, expectedDocument As Document, inlineHints As ImmutableArray(Of InlineHint)) As Task
62+
Private Shared Async Function ValidateDoubleClick(document As Document, expectedDocument As Document, inlineHints As ArrayBuilder(Of InlineHint)) As Task
6063
Dim textChanges = New List(Of TextChange)
6164
For Each inlineHint In inlineHints
6265
If inlineHint.ReplacementTextChange IsNot Nothing Then
@@ -88,8 +91,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.InlineHints
8891
Dim tagService = document.GetRequiredLanguageService(Of IInlineTypeHintsService)
8992

9093
Dim span = If(hostDocument.SelectedSpans.Any(), hostDocument.SelectedSpans.Single(), New TextSpan(0, snapshot.Length))
91-
Dim typeHints = Await tagService.GetInlineHintsAsync(
92-
document, span, options, displayOptions, displayAllOverride:=ephemeral, CancellationToken.None)
94+
Dim typeHints = ArrayBuilder(Of InlineHint).GetInstance()
95+
96+
Await tagService.AddInlineHintsAsync(
97+
document, span, options, displayOptions, displayAllOverride:=ephemeral, typeHints, CancellationToken.None)
9398

9499
Dim producedTags = From hint In typeHints
95100
Select hint.DisplayParts.GetFullText() + ":" + hint.Span.ToString()

src/Features/Core/Portable/InlineHints/AbstractInlineHintsService.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Immutable;
6-
using System.Linq;
76
using System.Threading;
87
using System.Threading.Tasks;
98
using Microsoft.CodeAnalysis.Host;
9+
using Microsoft.CodeAnalysis.PooledObjects;
1010
using Microsoft.CodeAnalysis.Shared.Extensions;
1111
using Microsoft.CodeAnalysis.Text;
1212

@@ -20,14 +20,19 @@ public async Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
2020
var inlineParameterService = document.GetLanguageService<IInlineParameterNameHintsService>();
2121
var inlineTypeService = document.GetLanguageService<IInlineTypeHintsService>();
2222

23-
var parameters = inlineParameterService == null
24-
? []
25-
: await inlineParameterService.GetInlineHintsAsync(document, textSpan, options.ParameterOptions, options.DisplayOptions, displayAllOverride, cancellationToken).ConfigureAwait(false);
23+
// Allow large array instances in the pool, as these arrays often exceed the ArrayBuilder reuse size threshold
24+
using var _ = ArrayBuilder<InlineHint>.GetInstance(discardLargeInstances: false, out var result);
2625

27-
var types = inlineTypeService == null
28-
? []
29-
: await inlineTypeService.GetInlineHintsAsync(document, textSpan, options.TypeOptions, options.DisplayOptions, displayAllOverride, cancellationToken).ConfigureAwait(false);
26+
if (inlineParameterService is not null)
27+
{
28+
await inlineParameterService.AddInlineHintsAsync(document, textSpan, options.ParameterOptions, options.DisplayOptions, displayAllOverride, result, cancellationToken).ConfigureAwait(false);
29+
}
3030

31-
return [.. parameters, .. types];
31+
if (inlineTypeService is not null)
32+
{
33+
await inlineTypeService.AddInlineHintsAsync(document, textSpan, options.TypeOptions, options.DisplayOptions, displayAllOverride, result, cancellationToken).ConfigureAwait(false);
34+
}
35+
36+
return result.ToImmutableAndClear();
3237
}
3338
}

src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,24 @@ protected abstract void AddAllParameterNameHintLocations(
3232
protected abstract bool IsIndexer(SyntaxNode node, IParameterSymbol parameter);
3333
protected abstract string GetReplacementText(string parameterName);
3434

35-
public async Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
35+
public async Task AddInlineHintsAsync(
3636
Document document,
3737
TextSpan textSpan,
3838
InlineParameterHintsOptions options,
3939
SymbolDescriptionOptions displayOptions,
4040
bool displayAllOverride,
41+
ArrayBuilder<InlineHint> result,
4142
CancellationToken cancellationToken)
4243
{
4344
var enabledForParameters = displayAllOverride || options.EnabledForParameters;
4445
if (!enabledForParameters)
45-
return [];
46+
return;
4647

4748
var literalParameters = displayAllOverride || options.ForLiteralParameters;
4849
var objectCreationParameters = displayAllOverride || options.ForObjectCreationParameters;
4950
var otherParameters = displayAllOverride || options.ForOtherParameters;
5051
if (!literalParameters && !objectCreationParameters && !otherParameters)
51-
return [];
52+
return;
5253

5354
var indexerParameters = displayAllOverride || options.ForIndexerParameters;
5455
var suppressForParametersThatDifferOnlyBySuffix = !displayAllOverride && options.SuppressForParametersThatDifferOnlyBySuffix;
@@ -59,9 +60,7 @@ public async Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
5960
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
6061
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
6162

62-
// Allow large array instances in the pool, as these arrays often exceed the ArrayBuilder reuse size threshold
63-
using var _1 = ArrayBuilder<InlineHint>.GetInstance(discardLargeInstances: false, out var result);
64-
using var _2 = ArrayBuilder<(int position, SyntaxNode argument, IParameterSymbol? parameter, HintKind kind)>.GetInstance(out var buffer);
63+
using var _ = ArrayBuilder<(int position, SyntaxNode argument, IParameterSymbol? parameter, HintKind kind)>.GetInstance(out var buffer);
6564

6665
foreach (var node in root.DescendantNodes(textSpan, n => n.Span.IntersectsWith(textSpan)))
6766
{
@@ -75,7 +74,7 @@ public async Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
7574
}
7675
}
7776

78-
return result.ToImmutableAndClear();
77+
return;
7978

8079
void AddHintsIfAppropriate(SyntaxNode node)
8180
{

src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,31 +28,30 @@ internal abstract class AbstractInlineTypeHintsService : IInlineTypeHintsService
2828
bool forCollectionExpressions,
2929
CancellationToken cancellationToken);
3030

31-
public async Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
31+
public async Task AddInlineHintsAsync(
3232
Document document,
3333
TextSpan textSpan,
3434
InlineTypeHintsOptions options,
3535
SymbolDescriptionOptions displayOptions,
3636
bool displayAllOverride,
37+
ArrayBuilder<InlineHint> result,
3738
CancellationToken cancellationToken)
3839
{
3940
var enabledForTypes = options.EnabledForTypes;
4041
if (!enabledForTypes && !displayAllOverride)
41-
return [];
42+
return;
4243

4344
var forImplicitVariableTypes = enabledForTypes && options.ForImplicitVariableTypes;
4445
var forLambdaParameterTypes = enabledForTypes && options.ForLambdaParameterTypes;
4546
var forImplicitObjectCreation = enabledForTypes && options.ForImplicitObjectCreation;
4647
var forCollectionExpressions = enabledForTypes && options.ForCollectionExpressions;
4748
if (!forImplicitVariableTypes && !forLambdaParameterTypes && !forImplicitObjectCreation && !forCollectionExpressions && !displayAllOverride)
48-
return [];
49+
return;
4950

5051
var anonymousTypeService = document.GetRequiredLanguageService<IStructuralTypeDisplayService>();
5152
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
5253
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
5354

54-
using var _1 = ArrayBuilder<InlineHint>.GetInstance(out var result);
55-
5655
foreach (var node in root.DescendantNodes(n => n.Span.IntersectsWith(textSpan)))
5756
{
5857
var hint = TryGetTypeHint(
@@ -97,8 +96,6 @@ public async Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
9796
span, taggedText, textChange, ranking: InlineHintsConstants.TypeRanking,
9897
InlineHintHelpers.GetDescriptionFunction(spanStart, type, displayOptions)));
9998
}
100-
101-
return result.ToImmutableAndClear();
10299
}
103100

104101
private static void AddParts(

src/Features/Core/Portable/InlineHints/IInlineParameterNameHintsService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System.Collections.Immutable;
65
using System.Threading;
76
using System.Threading.Tasks;
87
using Microsoft.CodeAnalysis.Host;
98
using Microsoft.CodeAnalysis.LanguageService;
9+
using Microsoft.CodeAnalysis.PooledObjects;
1010
using Microsoft.CodeAnalysis.Text;
1111

1212
namespace Microsoft.CodeAnalysis.InlineHints;
@@ -17,11 +17,12 @@ namespace Microsoft.CodeAnalysis.InlineHints;
1717
/// </summary>
1818
internal interface IInlineParameterNameHintsService : ILanguageService
1919
{
20-
Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
20+
Task AddInlineHintsAsync(
2121
Document document,
2222
TextSpan textSpan,
2323
InlineParameterHintsOptions options,
2424
SymbolDescriptionOptions displayOptions,
2525
bool displayAllOverride,
26+
ArrayBuilder<InlineHint> result,
2627
CancellationToken cancellationToken);
2728
}

src/Features/Core/Portable/InlineHints/IInlineTypeHintsService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System.Collections.Immutable;
65
using System.Threading;
76
using System.Threading.Tasks;
87
using Microsoft.CodeAnalysis.Host;
98
using Microsoft.CodeAnalysis.LanguageService;
9+
using Microsoft.CodeAnalysis.PooledObjects;
1010
using Microsoft.CodeAnalysis.Text;
1111

1212
namespace Microsoft.CodeAnalysis.InlineHints;
@@ -17,11 +17,12 @@ namespace Microsoft.CodeAnalysis.InlineHints;
1717
/// </summary>
1818
internal interface IInlineTypeHintsService : ILanguageService
1919
{
20-
Task<ImmutableArray<InlineHint>> GetInlineHintsAsync(
20+
Task AddInlineHintsAsync(
2121
Document document,
2222
TextSpan textSpan,
2323
InlineTypeHintsOptions options,
2424
SymbolDescriptionOptions displayOptions,
2525
bool displayAllOverride,
26+
ArrayBuilder<InlineHint> result,
2627
CancellationToken cancellationToken);
2728
}

0 commit comments

Comments
 (0)