Skip to content

Commit

Permalink
Immediately filter syntax trees down to those that either have attrib…
Browse files Browse the repository at this point in the history
…utes or global-using-aliases. (dotnet#62483)

* Cache the hash in compilation options

* Remove

* Update src/Compilers/Core/Portable/InternalUtilities/Hash.cs

* Update src/Compilers/Core/Portable/InternalUtilities/Hash.cs

* Update src/Compilers/Core/Portable/InternalUtilities/Hash.cs

* Fix api

* Walk green-nodes in incremental-generator attribute-finding path

* Use green nodes

* REmove

* Fix tests

* Avoid allocating builder if not needed

* Simplify

* Only loop once

* Do not look for load/reference directives when there can't be any

* REmove use of Lazy in DeclarationTable for rarely used values.

* Remove lazy

* In progress

* Change the data-flow path for searching for attributes to prevent fewer intermediary tables.

* Remove unused type

* Simplify

* Update tests

* Make internal

* Update

* Update src/Compilers/Core/Portable/SourceGeneration/Nodes/NodeStateTable.cs

* Update comment

* Filter large tables before processing htem.

* Update tests

* Sort

* Fix grammar

* Fix assert

* Add back in

* Revert

* Simplify

* remove

* Simplify

* Remove

* Simplify global aliases

* Prefilter to only trees that have attributes or globalaliases in them

* INcrease

* Only check the top level for global usings.

* Remove comment

* Simplify

* Add docs

* Revert

* Make static

* Extract common code

* PR feedback

* Use helper

* update run counts

* Chain compilations

* Fix grammar

* Don't use implicit construction

* Fix

* Tweak benchmark

* Simplify walk
  • Loading branch information
CyrusNajmabadi committed Jul 12, 2022
1 parent 2cd995d commit 7e2478f
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1373,8 +1373,7 @@ class XAttribute : System.Attribute
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttributeInternal"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason);
Expand Down Expand Up @@ -1419,8 +1418,7 @@ class XAttribute : System.Attribute
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttributeInternal"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Modified, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason);
Expand Down Expand Up @@ -1469,8 +1467,7 @@ class XAttribute : System.Attribute
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Collection(runResult.TrackedSteps["result_ForAttributeInternal"].Single().Outputs,
t => Assert.Equal(IncrementalStepRunReason.Cached, t.Reason));
Expand Down Expand Up @@ -1522,8 +1519,7 @@ class XAttribute : System.Attribute
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
Assert.Collection(runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"],
s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason),
s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason));
Expand Down Expand Up @@ -1633,8 +1629,7 @@ class C { }
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["result_ForAttributeInternal"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["compilationAndGroupedNodes_ForAttributeWithMetadataName"].Single().Outputs.Single().Reason);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ class C { }
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Removed, o.Reason));
Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Removed, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason);
}
Expand Down Expand Up @@ -1358,18 +1358,17 @@ class C { }
step => Assert.True(step.Outputs.Single().Value is ClassDeclarationSyntax { Identifier.ValueText: "C" }));

Assert.Collection(runResult.TrackedSteps["individualFileGlobalAliases_ForAttribute"],
s => Assert.Equal(IncrementalStepRunReason.New, s.Outputs.Single().Reason),
s => Assert.Equal(IncrementalStepRunReason.Unchanged, s.Outputs.Single().Reason));
Assert.Equal(IncrementalStepRunReason.Unchanged, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason),
s => Assert.Equal(IncrementalStepRunReason.Cached, s.Outputs.Single().Reason));
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["collectedGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["allUpGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);

Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Removed, o.Reason));
Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.New, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason);
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason);
}

[Fact]
Expand Down Expand Up @@ -1504,8 +1503,7 @@ class D { }"))));
Assert.Collection(runResult.TrackedSteps["compilationUnit_ForAttribute"].Single().Outputs,
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason),
o => Assert.Equal(IncrementalStepRunReason.Modified, o.Reason));
o => Assert.Equal(IncrementalStepRunReason.Unchanged, o.Reason));
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["compilationUnitAndGlobalAliases_ForAttribute"].Single().Outputs.Single().Reason);
Assert.Equal(IncrementalStepRunReason.Cached, runResult.TrackedSteps["result_ForAttribute"].Single().Outputs.Single().Reason);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public void Node_Builder_Can_Add_Entries_From_Previous_Table()
Assert.True(didRemoveEntries);
}

private IEnumerable<int> YieldItems(OneOrMany<int> items)
private static IEnumerable<int> YieldItems(OneOrMany<int> items)
{
foreach (var value in items)
yield return value;
Expand Down
16 changes: 16 additions & 0 deletions src/Compilers/Core/Portable/SourceGeneration/GlobalAliases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ public static GlobalAliases Create(ImmutableArray<(string aliasName, string symb
return aliasAndSymbolNames.IsEmpty ? Empty : new GlobalAliases(aliasAndSymbolNames);
}

public static GlobalAliases Create(ImmutableArray<GlobalAliases> aliasesArray)
{
if (aliasesArray.Length == 0)
return Empty;

if (aliasesArray.Length == 1)
return aliasesArray[0];

var total = ArrayBuilder<(string aliasName, string symbolName)>.GetInstance(aliasesArray.Sum(a => a.AliasAndSymbolNames.Length));

foreach (var array in aliasesArray)
total.AddRange(array.AliasAndSymbolNames);

return Create(total.ToImmutableAndFree());
}

public static GlobalAliases Concat(GlobalAliases ga1, GlobalAliases ga2)
{
if (ga1.AliasAndSymbolNames.Length == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,18 +629,19 @@ public TableEntry ToImmutableAndFree()
Debug.Assert(_items.Count == _requestedCapacity);
Debug.Assert(_states == null || _states.Count == _requestedCapacity);

var states = _states?.ToImmutableAndFree() ?? GetSingleArray(_currentState.Value);

OneOrMany<T> items;
if (_items.Count == 1)
{
var item = _items[0];
_items.Free();
return new TableEntry(OneOrMany.Create(item), states);
items = OneOrMany.Create(item);
}
else
{
return new TableEntry(OneOrMany.Create(_items.ToImmutableAndFree()), states);
items = OneOrMany.Create(_items.ToImmutableAndFree());
}

return new TableEntry(items, _states?.ToImmutableAndFree() ?? GetSingleArray(_currentState.Value));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Transactions;
Expand All @@ -20,7 +21,24 @@ namespace Microsoft.CodeAnalysis;

public partial struct SyntaxValueProvider
{
private static readonly ObjectPool<Stack<string>> s_stackPool = new(static () => new());
/// <summary>
/// Information computed about a particular tree. Cached so we don't repeatedly recompute this important
/// information each time the incremental pipeline is rerun.
/// </summary>
private record SyntaxTreeInfo(SyntaxTree Tree, bool ContainsGlobalAliases, bool ContainsAttributeList);

/// <summary>
/// Caching of syntax-tree to the info we've computed about it. Used because compilations will have thousands of
/// trees, and the incremental pipeline will get called back for *all* of them each time a compilation changes. We
/// do not want to continually recompute this data over and over again each time that happens given that normally
/// only one tree will be different. We also do not want to create an IncrementalValuesProvider that yield this
/// information as that will mean we have a node in the tree that scales with the number of *all syntax trees*, not
/// the number of *relevant syntax trees*. This can lead to huge memory churn keeping track of a high number of
/// trees, most of which are not going to be relevant.
/// </summary>
private static readonly ConditionalWeakTable<SyntaxTree, SyntaxTreeInfo> s_treeToInfo = new ConditionalWeakTable<SyntaxTree, SyntaxTreeInfo>();

private static readonly ObjectPool<Stack<string>> s_stackPool = new ObjectPool<Stack<string>>(static () => new Stack<string>());

/// <summary>
/// Returns all syntax nodes of that match <paramref name="predicate"/> if that node has an attribute on it that
Expand Down Expand Up @@ -54,17 +72,15 @@ public partial struct SyntaxValueProvider
// changed. CreateSyntaxProvider will have to rerun all incremental nodes since it passes along the
// SemanticModel, and that model is updated whenever any tree changes (since it is tied to the compilation).
var syntaxTreesProvider = _context.CompilationProvider
.SelectMany(static (c, _) => c.SyntaxTrees)
.SelectMany((compilation, cancellationToken) => compilation.SyntaxTrees
.Select(tree => GetTreeInfo(tree, syntaxHelper, cancellationToken))
.Where(info => info.ContainsGlobalAliases || info.ContainsAttributeList))
.WithTrackingName("compilationUnit_ForAttribute");

// Create a provider that provides (and updates) the global aliases for any particular file when it is edited.
var individualFileGlobalAliasesProvider = syntaxTreesProvider
.Where((tree, cancellationToken) =>
{
var root = tree.GetRoot(cancellationToken);
return syntaxHelper.ContainsGlobalAliases(root);
})
.Select((tree, cancellationToken) => getGlobalAliasesInCompilationUnit(syntaxHelper, tree.GetRoot(cancellationToken)))
.Where((info, _) => info.ContainsGlobalAliases)
.Select((info, cancellationToken) => getGlobalAliasesInCompilationUnit(syntaxHelper, info.Tree.GetRoot(cancellationToken)))
.WithTrackingName("individualFileGlobalAliases_ForAttribute");

// Create an aggregated view of all global aliases across all files. This should only update when an individual
Expand All @@ -75,7 +91,7 @@ public partial struct SyntaxValueProvider
.WithTrackingName("collectedGlobalAliases_ForAttribute");

var allUpGlobalAliasesProvider = collectedGlobalAliasesProvider
.Select(static (arrays, _) => GlobalAliases.Create(arrays.SelectMany(a => a.AliasAndSymbolNames).ToImmutableArray()))
.Select(static (arrays, _) => GlobalAliases.Create(arrays))
.WithTrackingName("allUpGlobalAliases_ForAttribute");

// Regenerate our data if the compilation options changed. VB can supply global aliases with compilation options,
Expand All @@ -96,19 +112,12 @@ public partial struct SyntaxValueProvider
// Combine the two providers so that we reanalyze every file if the global aliases change, or we reanalyze a
// particular file when it's compilation unit changes.
var syntaxTreeAndGlobalAliasesProvider = syntaxTreesProvider
.Where((tree, cancellationToken) =>
{
// Walk the green node tree first to avoid allocating the entire red tree for files that have no attributes.
//
// Don't bother looking in trees that don't even have attributes in them.
var root = tree.GetRoot(cancellationToken);
return ContainsAttributeList(root.Green, syntaxHelper.AttributeListKind);
})
.Where((info, _) => info.ContainsAttributeList)
.Combine(allUpGlobalAliasesProvider)
.WithTrackingName("compilationUnitAndGlobalAliases_ForAttribute");

return syntaxTreeAndGlobalAliasesProvider.Select(
(tuple, c) => (tuple.Left, GetMatchingNodes(syntaxHelper, tuple.Right, tuple.Left, simpleName, predicate, c)))
return syntaxTreeAndGlobalAliasesProvider
.Select((tuple, c) => (tuple.Left.Tree, GetMatchingNodes(syntaxHelper, tuple.Right, tuple.Left.Tree, simpleName, predicate, c)))
.Where(tuple => tuple.Item2.Length > 0)
.WithTrackingName("result_ForAttributeInternal");

Expand All @@ -125,6 +134,25 @@ static GlobalAliases getGlobalAliasesInCompilationUnit(
}
}

private static SyntaxTreeInfo GetTreeInfo(
SyntaxTree tree, ISyntaxHelper syntaxHelper, CancellationToken cancellationToken)
{
// prevent captures for the case where the item is in the tree.
return s_treeToInfo.TryGetValue(tree, out var info)
? info
: computeTreeInfo();

SyntaxTreeInfo computeTreeInfo()
{
var root = tree.GetRoot(cancellationToken);
var containsGlobalAliases = syntaxHelper.ContainsGlobalAliases(root);
var containsAttributeList = ContainsAttributeList(root.Green, syntaxHelper.AttributeListKind);

var info = new SyntaxTreeInfo(tree, containsGlobalAliases, containsAttributeList);
return s_treeToInfo.GetValue(tree, _ => info);
}
}

private static ImmutableArray<SyntaxNode> GetMatchingNodes(
ISyntaxHelper syntaxHelper,
GlobalAliases globalAliases,
Expand Down Expand Up @@ -306,10 +334,12 @@ private static bool ContainsAttributeList(GreenNode node, int attributeListKind)
if (node.RawKind == attributeListKind)
return true;

foreach (var child in node.ChildNodesAndTokens())
for (int i = 0, n = node.SlotCount; i < n; i++)
{
if (node.IsToken)
return false;
var child = node.GetSlot(i);

if (child is null || child.IsToken)
continue;

if (ContainsAttributeList(child, attributeListKind))
return true;
Expand Down
Loading

0 comments on commit 7e2478f

Please sign in to comment.