Skip to content

Commit 63e529a

Browse files
authored
Merge pull request #4918 from MDoerner/SpeedUpPArsing
Speed up parsing (slightly)
2 parents 10adc78 + eed6789 commit 63e529a

File tree

12 files changed

+92
-53
lines changed

12 files changed

+92
-53
lines changed

Rubberduck.CodeAnalysis/CodeMetrics/CodeMetricsAnalyst.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Antlr4.Runtime.Tree;
2-
using Rubberduck.Parsing.Symbols;
32
using Rubberduck.Parsing.VBA;
43
using Rubberduck.VBEditor;
54
using System.Collections.Generic;
@@ -14,11 +13,11 @@ public class CodeMetricsAnalyst : ICodeMetricsAnalyst
1413
{
1514
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
1615

17-
private readonly CodeMetric[] metrics;
16+
private readonly CodeMetric[] _metrics;
1817

1918
public CodeMetricsAnalyst(IEnumerable<CodeMetric> supportedMetrics)
2019
{
21-
metrics = supportedMetrics.ToArray();
20+
_metrics = supportedMetrics.ToArray();
2221
}
2322

2423
public IEnumerable<ICodeMetricResult> GetMetrics(RubberduckParserState state)
@@ -45,7 +44,7 @@ public IEnumerable<ICodeMetricResult> ModuleResults(QualifiedModuleName moduleNa
4544

4645
private IEnumerable<ICodeMetricResult> TraverseModuleTree(IParseTree parseTree, DeclarationFinder declarationFinder, QualifiedModuleName moduleName)
4746
{
48-
var listeners = metrics.Select(metric => metric.TreeListener).ToList();
47+
var listeners = _metrics.Select(metric => metric.TreeListener).ToList();
4948
foreach (var l in listeners)
5049
{
5150
l.Reset();

Rubberduck.CodeAnalysis/CodeMetrics/NestingLevelMetric.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
using Antlr4.Runtime.Misc;
22
using Rubberduck.Parsing.Grammar;
33
using Rubberduck.Parsing.Symbols;
4-
using System;
54
using System.Collections.Generic;
65
using System.Diagnostics;
76
using System.Linq;
8-
using System.Text;
9-
using System.Threading.Tasks;
107

118
namespace Rubberduck.CodeAnalysis.CodeMetrics
129
{

Rubberduck.Core/CodeAnalysis/CodeMetrics/CodeMetricsViewModel.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ private void Synchronize(IEnumerable<Declaration> declarations)
6565
var metricResults = _analyst.GetMetrics(_state);
6666
_resultsByDeclaration = metricResults.GroupBy(r => r.Declaration).ToDictionary(g => g.Key, g => g.ToList());
6767

68-
_uiDispatcher.Invoke(() =>
68+
//We have to wait for the task to guarantee that no new parse starts invalidating all cached components.
69+
_uiDispatcher.StartTask(() =>
6970
{
7071
var updates = declarations.ToList();
7172
var existing = Projects.OfType<CodeExplorerProjectViewModel>().ToList();
@@ -86,7 +87,7 @@ private void Synchronize(IEnumerable<Declaration> declarations)
8687
var model = new CodeExplorerProjectViewModel(project, ref updates, _state, _vbe, false);
8788
Projects.Add(model);
8889
}
89-
});
90+
}).Wait();
9091
}
9192

9293
private ICodeExplorerNode _selectedItem;

Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerViewModel.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
231231
if (e.State == ParserState.Ready)
232232
{
233233
// Finished up resolving references, so we can now update the reference nodes.
234-
_uiDispatcher.Invoke(() =>
234+
//We have to wait for the task to guarantee that no new parse starts invalidating all cached components.
235+
_uiDispatcher.StartTask(() =>
235236
{
236237
var referenceFolders = Projects.SelectMany(node =>
237238
node.Children.OfType<CodeExplorerReferenceFolderViewModel>());
@@ -243,7 +244,7 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
243244
Unparsed = !Projects.Any();
244245
IsBusy = false;
245246
ParserReady = true;
246-
});
247+
}).Wait();
247248
return;
248249
}
249250

@@ -264,7 +265,8 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
264265
/// </param>
265266
private void Synchronize(IEnumerable<Declaration> declarations)
266267
{
267-
_uiDispatcher.Invoke(() =>
268+
//We have to wait for the task to guarantee that no new parse starts invalidating all cached components.
269+
_uiDispatcher.StartTask(() =>
268270
{
269271
var updates = declarations.ToList();
270272
var existing = Projects.OfType<CodeExplorerProjectViewModel>().ToList();
@@ -287,7 +289,7 @@ private void Synchronize(IEnumerable<Declaration> declarations)
287289
}
288290

289291
CanSearch = Projects.Any();
290-
});
292+
}).Wait();
291293
}
292294

293295
private void ParserState_ModuleStateChanged(object sender, ParseProgressEventArgs e)

Rubberduck.Parsing/Common/ParsingStageTimer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static ParsingStageTimer StartNew()
2222

2323
public void Log(string message)
2424
{
25-
Logger.Debug(message, _stopwatch.ElapsedMilliseconds);
25+
Logger.Info(message, _stopwatch.ElapsedMilliseconds);
2626
}
2727
}
2828
}

Rubberduck.Parsing/VBA/ParseCoordinator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ private void ParseAll(object requestor)
436436
ParsingSuspendLock.ExitReadLock();
437437
}
438438
}
439-
if (watch != null) Logger.Debug("Parsing run finished after {0}s. (thread {1}).", watch.Elapsed.TotalSeconds, Thread.CurrentThread.ManagedThreadId);
439+
if (watch != null) Logger.Info("Parsing run finished after {0}s. (thread {1}).", watch.Elapsed.TotalSeconds, Thread.CurrentThread.ManagedThreadId);
440440
}
441441

442442
protected void ParseAllInternal(object requestor, CancellationToken token)

Rubberduck.Parsing/VBA/Parsing/IModuleParser.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Antlr4.Runtime;
44
using Antlr4.Runtime.Tree;
55
using Rubberduck.Parsing.Annotations;
6-
using Rubberduck.Parsing.Rewriter;
76
using Rubberduck.Parsing.Symbols;
87
using Rubberduck.VBEditor;
98

Rubberduck.Parsing/VBA/Parsing/ParseRunner.cs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Linq;
44
using System.Threading;
55
using System.Threading.Tasks;
6-
using Rubberduck.Parsing.Common;
76
using Rubberduck.VBEditor;
87

98
namespace Rubberduck.Parsing.VBA.Parsing
@@ -21,26 +20,41 @@ public ParseRunner(
2120
parser)
2221
{ }
2322

24-
public override void ParseModules(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token)
23+
protected override IReadOnlyCollection<(QualifiedModuleName module, ModuleParseResults results)> ModulePareResults(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token)
2524
{
2625
if (!modules.Any())
2726
{
28-
return;
27+
return new List<(QualifiedModuleName module, ModuleParseResults results)>();
2928
}
3029

3130
token.ThrowIfCancellationRequested();
3231

33-
var parsingStageTimer = ParsingStageTimer.StartNew();
32+
var results = new List<(QualifiedModuleName module, ModuleParseResults results)>();
33+
var lockObject = new object();
3434

35-
var options = new ParallelOptions();
36-
options.CancellationToken = token;
37-
options.MaxDegreeOfParallelism = _maxDegreeOfParserParallelism;
35+
var options = new ParallelOptions
36+
{
37+
CancellationToken = token,
38+
MaxDegreeOfParallelism = _maxDegreeOfParserParallelism
39+
};
3840

3941
try
4042
{
4143
Parallel.ForEach(modules,
4244
options,
43-
module => ParseModule(module, token)
45+
() => new List<(QualifiedModuleName module, ModuleParseResults results)>(),
46+
(module, state, localList) =>
47+
{
48+
localList.Add((module, ModuleParseResults(module, token)));
49+
return localList;
50+
},
51+
(finalResult) =>
52+
{
53+
lock (lockObject)
54+
{
55+
results.AddRange(finalResult);
56+
}
57+
}
4458
);
4559
}
4660
catch (AggregateException exception)
@@ -53,8 +67,7 @@ public override void ParseModules(IReadOnlyCollection<QualifiedModuleName> modul
5367
throw;
5468
}
5569

56-
parsingStageTimer.Stop();
57-
parsingStageTimer.Log("Parsed user modules in {0}ms.");
70+
return results;
5871
}
5972
}
6073
}

Rubberduck.Parsing/VBA/Parsing/ParseRunnerBase.cs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Threading;
4+
using Rubberduck.Parsing.Common;
45
using Rubberduck.Parsing.VBA.Parsing.ParsingExceptions;
56
using Rubberduck.VBEditor;
67

@@ -36,23 +37,32 @@ protected ParseRunnerBase(
3637
_parser = parser;
3738
}
3839

40+
public void ParseModules(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token)
41+
{
42+
var parsingStageTimer = ParsingStageTimer.StartNew();
43+
44+
var parseResults = ModulePareResults(modules, token);
45+
SaveModuleParseResultsOnState(parseResults, token);
3946

40-
public abstract void ParseModules(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token);
47+
parsingStageTimer.Stop();
48+
parsingStageTimer.Log("Parsed user modules in {0}ms.");
49+
}
4150

51+
protected abstract IReadOnlyCollection<(QualifiedModuleName module, ModuleParseResults results)> ModulePareResults(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token);
4252

43-
protected void ParseModule(QualifiedModuleName module, CancellationToken token)
53+
protected ModuleParseResults ModuleParseResults(QualifiedModuleName module, CancellationToken token)
4454
{
4555
_state.ClearStateCache(module);
4656
try
4757
{
48-
var parseResults = _parser.Parse(module, token);
49-
SaveModuleParseResultsOnState(module, parseResults, token);
58+
return _parser.Parse(module, token);
5059
}
5160
catch (SyntaxErrorException syntaxErrorException)
5261
{
5362
//In contrast to the situation in the success scenario, the overall parser state is reevaluated immediately.
54-
//This sets the state directly on the state because it is the sole instance where we have to pass the SyntaxErorException.
63+
//This sets the state directly on the state because it is the sole instance where we have to pass the SyntaxErrorException.
5564
_state.SetModuleState(module, ParserState.Error, token, syntaxErrorException);
65+
return default;
5666
}
5767
catch (Exception exception)
5868
{
@@ -61,30 +71,40 @@ protected void ParseModule(QualifiedModuleName module, CancellationToken token)
6171
}
6272
}
6373

64-
private void SaveModuleParseResultsOnState(QualifiedModuleName module, ModuleParseResults results, CancellationToken token)
74+
private void SaveModuleParseResultsOnState(IReadOnlyCollection<(QualifiedModuleName module, ModuleParseResults results)> parseResults, CancellationToken token)
6575
{
66-
lock (_state)
76+
foreach (var (module, result) in parseResults)
6777
{
68-
token.ThrowIfCancellationRequested();
78+
if (result.CodePaneParseTree == null)
79+
{
80+
continue;
81+
}
6982

70-
//This has to come first because it creates the module state if not present.
71-
_state.AddModuleStateIfNotPresent(module);
83+
SaveModuleParseResultsOnState(module, result, token);
84+
}
85+
}
7286

73-
_state.SaveContentHash(module);
74-
_state.AddParseTree(module, results.CodePaneParseTree);
75-
_state.AddParseTree(module, results.AttributesParseTree, CodeKind.AttributesCode);
76-
_state.SetModuleComments(module, results.Comments);
77-
_state.SetModuleAnnotations(module, results.Annotations);
78-
_state.SetModuleAttributes(module, results.Attributes);
79-
_state.SetMembersAllowingAttributes(module, results.MembersAllowingAttributes);
80-
_state.SetCodePaneTokenStream(module, results.CodePaneTokenStream);
81-
_state.SetAttributesTokenStream(module, results.AttributesTokenStream);
87+
private void SaveModuleParseResultsOnState(QualifiedModuleName module, ModuleParseResults results, CancellationToken token)
88+
{
89+
token.ThrowIfCancellationRequested();
8290

83-
// This really needs to go last
84-
//It does not reevaluate the overall parer state to avoid concurrent evaluation of all module states and for performance reasons.
85-
//The evaluation has to be triggered manually in the calling procedure.
86-
StateManager.SetModuleState(module, ParserState.Parsed, token, false); //Note that this is ok because locks allow re-entrancy.
87-
}
91+
//This has to come first because it creates the module state if not present.
92+
_state.AddModuleStateIfNotPresent(module);
93+
94+
_state.SaveContentHash(module);
95+
_state.AddParseTree(module, results.CodePaneParseTree);
96+
_state.AddParseTree(module, results.AttributesParseTree, CodeKind.AttributesCode);
97+
_state.SetModuleComments(module, results.Comments);
98+
_state.SetModuleAnnotations(module, results.Annotations);
99+
_state.SetModuleAttributes(module, results.Attributes);
100+
_state.SetMembersAllowingAttributes(module, results.MembersAllowingAttributes);
101+
_state.SetCodePaneTokenStream(module, results.CodePaneTokenStream);
102+
_state.SetAttributesTokenStream(module, results.AttributesTokenStream);
103+
104+
// This really needs to go last
105+
//It does not reevaluate the overall parer state to avoid concurrent evaluation of all module states and for performance reasons.
106+
//The evaluation has to be triggered manually in the calling procedure.
107+
StateManager.SetModuleState(module, ParserState.Parsed, token,false); //Note that this is ok because locks allow re-entrancy.
88108
}
89109
}
90110
}

Rubberduck.Parsing/VBA/Parsing/SynchronousParseRunner.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,22 @@ public SynchronousParseRunner(
1818
{ }
1919

2020

21-
public override void ParseModules(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token)
21+
protected override IReadOnlyCollection<(QualifiedModuleName module, ModuleParseResults results)> ModulePareResults(IReadOnlyCollection<QualifiedModuleName> modules, CancellationToken token)
2222
{
2323
if (!modules.Any())
2424
{
25-
return;
25+
return new List<(QualifiedModuleName module, ModuleParseResults results)>();
2626
}
2727

2828
token.ThrowIfCancellationRequested();
2929

30+
var results = new List<(QualifiedModuleName module, ModuleParseResults results)>();
31+
3032
try
3133
{
3234
foreach (var module in modules)
3335
{
34-
ParseModule(module, token);
36+
results.Add((module, ModuleParseResults(module, token)));
3537
}
3638
}
3739
catch (OperationCanceledException)
@@ -43,6 +45,8 @@ public override void ParseModules(IReadOnlyCollection<QualifiedModuleName> modul
4345
StateManager.SetStatusAndFireStateChanged(this, ParserState.Error, token);
4446
throw;
4547
}
48+
49+
return results;
4650
}
4751
}
4852
}

0 commit comments

Comments
 (0)