Skip to content

Commit 64275c2

Browse files
committed
Cleaned up some perforance issues.
Added code to find end of a lexer token. Syntax highlighting is operational via an IEditorGuide (heuristic to follow).
1 parent bc15ffd commit 64275c2

File tree

8 files changed

+258
-105
lines changed

8 files changed

+258
-105
lines changed

GunWin/App.config

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
55
</startup>
66
<appSettings>
7-
<add key="MaximumRealtimeRenderNodes" value="50"/>
7+
<add key="NodeThresholdCountForThrottling" value="50"/>
8+
<add key="MillisecondsToDelayPerNodeWhenThrottling" value="5"/>
9+
<add key="MaximumRenderShortDelay" value="1000"/>
10+
<add key="MinimumRenderCountToTriggerLongDelay" value="10"/>
811
</appSettings>
912
</configuration>

GunWin/EditorSettings.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#region BSD 3-Clause License
2+
// <copyright file="EditorSettings.cs" company="Edgerunner.org">
3+
// Copyright 2020
4+
// </copyright>
5+
//
6+
// BSD 3-Clause License
7+
//
8+
// Copyright (c) 2020,
9+
// All rights reserved.
10+
//
11+
// Redistribution and use in source and binary forms, with or without
12+
// modification, are permitted provided that the following conditions are met:
13+
//
14+
// 1. Redistributions of source code must retain the above copyright notice, this
15+
// list of conditions and the following disclaimer.
16+
//
17+
// 2. Redistributions in binary form must reproduce the above copyright notice,
18+
// this list of conditions and the following disclaimer in the documentation
19+
// and/or other materials provided with the distribution.
20+
//
21+
// 3. Neither the name of the copyright holder nor the names of its
22+
// contributors may be used to endorse or promote products derived from
23+
// this software without specific prior written permission.
24+
//
25+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35+
#endregion
36+
37+
namespace Org.Edgerunner.ANTLR4.Tools.Testing.GrunWin
38+
{
39+
/// <summary>
40+
/// Class that represents various code editor settings.
41+
/// </summary>
42+
public class EditorSettings
43+
{
44+
/// <summary>
45+
/// Gets or sets the node threshold count for throttling.
46+
/// </summary>
47+
/// <value>The node threshold count for throttling.</value>
48+
public int NodeThresholdCountForThrottling { get; set; }
49+
50+
/// <summary>
51+
/// Gets or sets the milliseconds to delay per node when throttling.
52+
/// </summary>
53+
/// <value>The milliseconds to delay per node when throttling.</value>
54+
public int MillisecondsToDelayPerNodeWhenThrottling { get; set; }
55+
56+
/// <summary>
57+
/// Gets or sets the maximum render short delay.
58+
/// </summary>
59+
/// <value>The maximum render short delay.</value>
60+
public int MaximumRenderShortDelay { get; set; }
61+
62+
/// <summary>
63+
/// Gets or sets the minimum render count to trigger a long delay.
64+
/// </summary>
65+
/// <value>The minimum render count to trigger a long delay.</value>
66+
public int MinimumRenderCountToTriggerLongDelay { get; set; }
67+
}
68+
}

GunWin/EditorSyntaxHighlighter.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646

4747
namespace Org.Edgerunner.ANTLR4.Tools.Testing.GrunWin
4848
{
49+
/// <summary>
50+
/// Class used to handle syntax highlighting of a FastColoredTextBox editor.
51+
/// </summary>
4952
public class EditorSyntaxHighlighter
5053
{
5154
private int _TokenColoringInProgress;
@@ -66,7 +69,7 @@ public void ColorizeTokens(FastColoredTextBox editor, StyleRegistry registry, IL
6669
foreach (var token in tokens)
6770
{
6871
var startingPlace = new Place(token.ActualToken.Column, token.ActualToken.Line - 1);
69-
var stoppingPlace = new Place(token.ActualToken.Column + token.Text.Length, token.ActualToken.Line - 1);
72+
var stoppingPlace = new Place(token.EndingColumnPosition - 1, token.EndingLineNumber - 1);
7073
var tokenRange = editor.GetRange(startingPlace, stoppingPlace);
7174
tokenRange.ClearStyle(StyleIndex.All);
7275
var style = registry.GetTokenStyle(token);

GunWin/GraphWorker.cs

Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#region BSD 3-Clause License
2+
23
// <copyright file="GraphWorker.cs" company="Edgerunner.org">
3-
// Copyright 2020 Thaddeus Ryker
4+
// Copyright 2020
45
// </copyright>
56
//
67
// BSD 3-Clause License
78
//
8-
// Copyright (c) 2020, Thaddeus Ryker
9+
// Copyright (c) 2020,
910
// All rights reserved.
1011
//
1112
// Redistribution and use in source and binary forms, with or without
@@ -32,6 +33,7 @@
3233
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3334
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3435
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36+
3537
#endregion
3638

3739
using System;
@@ -50,53 +52,118 @@
5052
namespace Org.Edgerunner.ANTLR4.Tools.Testing.GrunWin
5153
{
5254
/// <summary>
53-
/// Class that handles the work of text in the background.
54-
/// Implements the <see cref="Org.Edgerunner.ANTLR4.Tools.Testing.GrunWin.IGraphWorker" />
55+
/// Class that handles the work of text in the background.
56+
/// Implements the <see cref="Org.Edgerunner.ANTLR4.Tools.Testing.GrunWin.IGraphWorker" />
5557
/// </summary>
5658
/// <remarks>This class is thread safe.</remarks>
5759
/// <seealso cref="Org.Edgerunner.ANTLR4.Tools.Testing.GrunWin.IGraphWorker" />
5860
public class GraphWorker : IGraphWorker
5961
{
62+
private readonly object _Padlock;
63+
64+
private readonly EditorSettings _Settings;
65+
6066
private readonly SynchronizationContext _SynchronizationContext;
6167

62-
private readonly object _Padlock;
68+
private DateTime _LastQueuedTime;
6369

6470
private int _PreviousNodeQty;
6571

66-
private DateTime _LastQueuedTime;
72+
#region Constructors And Finalizers
6773

6874
/// <summary>
69-
/// Initializes a new instance of the <see cref="GraphWorker" /> class.
75+
/// Initializes a new instance of the <see cref="GraphWorker" /> class.
7076
/// </summary>
7177
/// <param name="synchronizationContext">The synchronization context.</param>
72-
public GraphWorker([NotNull] SynchronizationContext synchronizationContext)
78+
/// <param name="settings">The settings.</param>
79+
/// <exception cref="ArgumentNullException">
80+
/// synchronizationContext
81+
/// or
82+
/// settings are null
83+
/// </exception>
84+
public GraphWorker([NotNull] SynchronizationContext synchronizationContext, [NotNull] EditorSettings settings)
7385
{
7486
_SynchronizationContext = synchronizationContext ?? throw new ArgumentNullException(nameof(synchronizationContext));
87+
_Settings = settings ?? throw new ArgumentNullException(nameof(settings));
7588
_PreviousNodeQty = 0;
7689
_Padlock = new object();
7790
QueuedWork = new Queue<GraphingWorkItem>();
7891
GraphingTask = new Task(GraphingWorkLoop);
7992
}
8093

81-
/// <inheritdoc/>
82-
public event EventHandler<GraphingResult> GraphingFinished;
94+
#endregion
8395

8496
/// <summary>
85-
/// Gets or sets the graphing task.
97+
/// Gets or sets the graphing task.
8698
/// </summary>
8799
/// <value>The graphing task.</value>
88100
private Task GraphingTask { get; set; }
89101

90102
private Queue<GraphingWorkItem> QueuedWork { get; }
91103

92-
private void PostGraphingFinishedEvent(object state)
104+
#region IGraphWorker Members
105+
106+
/// <inheritdoc />
107+
public bool CancelWork()
93108
{
94-
GraphingFinished?.Invoke(this, (GraphingResult)state);
109+
lock (_Padlock)
110+
{
111+
var hadWork = QueuedWork.Count != 0;
112+
QueuedWork.Clear();
113+
return hadWork;
114+
}
95115
}
96116

97-
private void OnGraphingFinished(GraphingResult result)
117+
/// <inheritdoc />
118+
public void Graph(IParseTreeGrapher grapher, ITree tree, IList<string> parserRules)
98119
{
99-
_SynchronizationContext.Post(PostGraphingFinishedEvent, result);
120+
lock (_Padlock)
121+
{
122+
var nextRun =
123+
CalculateNextRunTime(
124+
_PreviousNodeQty,
125+
_Settings.NodeThresholdCountForThrottling,
126+
_Settings.MillisecondsToDelayPerNodeWhenThrottling,
127+
_Settings.MaximumRenderShortDelay);
128+
var work = new GraphingWorkItem(tree, parserRules, grapher, nextRun);
129+
QueuedWork.Enqueue(work);
130+
_LastQueuedTime = DateTime.Now;
131+
if (GraphingTask.IsCompleted)
132+
GraphingTask = new Task(GraphingWorkLoop);
133+
if (GraphingTask.Status != TaskStatus.Running)
134+
GraphingTask.Start();
135+
}
136+
}
137+
138+
/// <inheritdoc />
139+
public event EventHandler<GraphingResult> GraphingFinished;
140+
141+
#endregion
142+
143+
private DateTime CalculateNextRunTime(
144+
int previousNodes,
145+
int minimumNodeThresholdToDelay,
146+
int millisecondsPerNodeToDelay,
147+
int maximumDelay)
148+
{
149+
// We use reactive throttling to avoid overwhelming the client with repeated parsing of large samples
150+
if (previousNodes < minimumNodeThresholdToDelay)
151+
return DateTime.Now;
152+
153+
var delay = Math.Min(maximumDelay, previousNodes * millisecondsPerNodeToDelay);
154+
return DateTime.Now + TimeSpan.FromMilliseconds(delay);
155+
}
156+
157+
private int CalculateTotalTreeNodes(ITree tree)
158+
{
159+
if (tree == null)
160+
return 0;
161+
162+
var result = 1;
163+
for (var i = 0; i < tree.ChildCount; i++)
164+
result += CalculateTotalTreeNodes(tree.GetChild(i));
165+
166+
return result;
100167
}
101168

102169
private void GraphingWorkLoop()
@@ -114,7 +181,7 @@ private void GraphingWorkLoop()
114181
lastQueued = _LastQueuedTime;
115182
}
116183

117-
if (workCount > 4 && (DateTime.Now - lastQueued) < TimeSpan.FromMilliseconds(1000))
184+
if (workCount > _Settings.MinimumRenderCountToTriggerLongDelay && (DateTime.Now - lastQueued) < TimeSpan.FromMilliseconds(500))
118185
{
119186
Thread.Sleep(500);
120187
continue;
@@ -126,14 +193,15 @@ private void GraphingWorkLoop()
126193
if (QueuedWork.Count == 0)
127194
return;
128195

129-
var work = new GraphingWorkItem();
130-
131196
// We really only care about the last work item, so that's all we are keeping
197+
// However, we use the "ParseWhen" target date/time for the first entry for our check
198+
var work = QueuedWork.Dequeue();
199+
var graphWhen = work.GraphWhen;
132200
while (QueuedWork.Count != 0)
133201
work = QueuedWork.Dequeue();
134202

135203
// If we should delay longer, we enqueue the last item again for later evaluation
136-
if (DateTime.Now < work.GraphWhen)
204+
if (DateTime.Now < graphWhen)
137205
{
138206
QueuedWork.Enqueue(work);
139207
continue;
@@ -146,33 +214,11 @@ private void GraphingWorkLoop()
146214
}
147215
}
148216

149-
private int CalculateTotalTreeNodes(ITree tree)
150-
{
151-
if (tree == null)
152-
return 0;
153-
154-
var result = 1;
155-
for (int i = 0; i < tree.ChildCount; i++)
156-
result += CalculateTotalTreeNodes(tree.GetChild(i));
157-
158-
return result;
159-
}
160-
161-
private DateTime CalculateNextRunTime(int previousNodes, int minimumNodeThresholdToDelay, int millisecondsPerNodeToDelay, int maximumDelay)
162-
{
163-
// We use reactive throttling to avoid overwhelming the client with repeated parsing of large samples
164-
if (previousNodes < minimumNodeThresholdToDelay)
165-
return DateTime.Now;
166-
167-
var delay = Math.Min(maximumDelay, previousNodes * millisecondsPerNodeToDelay);
168-
return DateTime.Now + TimeSpan.FromMilliseconds(delay);
169-
}
170-
171217
/// <summary>
172-
/// Handles the actual graphing.
218+
/// Handles the actual graphing.
173219
/// </summary>
174220
/// <param name="work">The work item to graph.</param>
175-
/// <returns>A new <see cref="Graph"/>.</returns>
221+
/// <returns>A new <see cref="Graph" />.</returns>
176222
private GraphingResult HandleGraphing(GraphingWorkItem work)
177223
{
178224
if (work.TreeGrapher == null || work.ParseTree == null)
@@ -184,31 +230,14 @@ private GraphingResult HandleGraphing(GraphingWorkItem work)
184230
return new GraphingResult(graph, work.ParseTree);
185231
}
186232

187-
/// <inheritdoc/>
188-
public void Graph(IParseTreeGrapher grapher, ITree tree, IList<string> parserRules)
233+
private void OnGraphingFinished(GraphingResult result)
189234
{
190-
lock (_Padlock)
191-
{
192-
var nextRun = CalculateNextRunTime(_PreviousNodeQty, 50, 5, 2000);
193-
var work = new GraphingWorkItem(tree, parserRules, grapher, nextRun);
194-
QueuedWork.Enqueue(work);
195-
_LastQueuedTime = DateTime.Now;
196-
if (GraphingTask.IsCompleted)
197-
GraphingTask = new Task(GraphingWorkLoop);
198-
if (GraphingTask.Status != TaskStatus.Running)
199-
GraphingTask.Start();
200-
}
235+
_SynchronizationContext.Post(PostGraphingFinishedEvent, result);
201236
}
202237

203-
/// <inheritdoc/>
204-
public bool CancelWork()
238+
private void PostGraphingFinishedEvent(object state)
205239
{
206-
lock (_Padlock)
207-
{
208-
var hadWork = QueuedWork.Count != 0;
209-
QueuedWork.Clear();
210-
return hadWork;
211-
}
240+
GraphingFinished?.Invoke(this, (GraphingResult)state);
212241
}
213242
}
214243
}

GunWin/GunWin.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<Reference Include="System.Xml" />
7878
</ItemGroup>
7979
<ItemGroup>
80+
<Compile Include="EditorSettings.cs" />
8081
<Compile Include="EditorSyntaxHighlighter.cs" />
8182
<Compile Include="GrammarSelector.cs">
8283
<SubType>Form</SubType>

0 commit comments

Comments
 (0)