Skip to content

Commit 959382b

Browse files
committed
wip: step cache.
1 parent feff96d commit 959382b

7 files changed

Lines changed: 228 additions & 116 deletions

File tree

NTDLS.ExpressionParser/Expression.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public Expression(string text, ExpressionOptions? options = null)
4848
}) ?? throw new Exception("Failed to create persistent cache.");
4949

5050
Sanitized = cached.Sanitized;
51-
State = cached.State.Clone();
51+
State = cached.State.Clone(cached.Sanitized);
5252
}
5353
else
5454
{
@@ -84,11 +84,11 @@ public Expression(string text, ExpressionOptions? options = null)
8484

8585
if (State.WorkingText[0] == '$')
8686
{
87-
State.HydrateTemplateParsedCache(_expressionHash);
88-
return State.GetPreComputedCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
87+
State.HydrateTemplateCache(_expressionHash);
88+
return State.GetPlaceholderCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
8989
}
9090

91-
State.HydrateTemplateParsedCache(_expressionHash);
91+
State.HydrateTemplateCache(_expressionHash);
9292
return StringToDouble(State.WorkingText);
9393
}
9494

@@ -130,13 +130,13 @@ public Expression(string text, ExpressionOptions? options = null)
130130
if (State.WorkingText[0] == '$')
131131
{
132132
showWork = work.ToString();
133-
State.HydrateTemplateParsedCache(_expressionHash);
134-
return State.GetPreComputedCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
133+
State.HydrateTemplateCache(_expressionHash);
134+
return State.GetPlaceholderCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
135135
}
136136

137137
showWork = work.ToString();
138138

139-
State.HydrateTemplateParsedCache(_expressionHash);
139+
State.HydrateTemplateCache(_expressionHash);
140140
return StringToDouble(State.WorkingText);
141141
}
142142

@@ -274,7 +274,7 @@ private string SwapInCacheValues(string text)
274274
if (begIndex >= 0 && endIndex > begIndex)
275275
{
276276
var cacheKey = copy.Substring(begIndex + 1, (endIndex - begIndex) - 1);
277-
copy = copy.Replace($"${cacheKey}$", State.GetPreComputedCacheItem(cacheKey).ComputedValue?.ToString(_precisionFormat) ?? "null");
277+
copy = copy.Replace($"${cacheKey}$", State.GetPlaceholderCacheItem(cacheKey).ComputedValue?.ToString(_precisionFormat) ?? "null");
278278
}
279279
else
280280
{
@@ -362,7 +362,7 @@ internal bool AcquireSubexpression(out int outStartIndex, out int outEndIndex, o
362362
}
363363
else if (span[0] == '$')
364364
{
365-
return State.GetPreComputedCacheItem(span[1..^1]).ComputedValue;
365+
return State.GetPlaceholderCacheItem(span[1..^1]).ComputedValue;
366366
}
367367

368368
if (Options.UseFastFloatingPointParser)

NTDLS.ExpressionParser/ExpressionState.cs

Lines changed: 128 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@ internal class ExpressionState
1212
public string WorkingText { get; set; } = string.Empty;
1313
public readonly StringBuilder Buffer;
1414

15+
private int _nextComputedStepSlot = 0;
16+
private int _consumedComputedStepSlots = 0;
17+
private ComputedStepItem[] _computedStep = [];
18+
private bool _isTemplateCacheHydrated = false;
19+
20+
private int _nextScanStepSlot = 0;
21+
private int _consumedScanStepSlots = 0;
22+
private ScanStepItem[] _scanStep = [];
23+
private bool _isScanStepHydrated = false;
24+
25+
private PlaceholderCacheItem[] _placeholderCache = [];
1526
private int _nextPlaceholderCacheSlot = 0;
16-
private PreComputedCacheItem[] _preComputedCache = [];
17-
private int _nextPreComputedCacheSlot = 0;
1827
private int _operationCount = 0;
19-
private PlaceholderCacheItem?[] _placeholderCache = [];
2028
private readonly ExpressionOptions _options;
21-
private bool _isPlaceholderCacheHydrated = false;
2229

2330
public ExpressionState(Sanitized sanitized, ExpressionOptions options)
2431
{
@@ -27,14 +34,17 @@ public ExpressionState(Sanitized sanitized, ExpressionOptions options)
2734

2835
WorkingText = sanitized.Text;
2936
_operationCount = sanitized.OperationCount;
30-
_nextPreComputedCacheSlot = sanitized.ConsumedPreComputedCacheSlots;
31-
_preComputedCache = new PreComputedCacheItem[sanitized.OperationCount];
32-
_placeholderCache = new PlaceholderCacheItem?[_operationCount];
33-
_nextPlaceholderCacheSlot = 0;
37+
_nextPlaceholderCacheSlot = sanitized.ConsumedPlaceholderCacheSlots;
38+
_placeholderCache = new PlaceholderCacheItem[sanitized.OperationCount];
39+
_computedStep = new ComputedStepItem[_operationCount];
40+
_nextComputedStepSlot = 0;
41+
42+
_scanStep = new ScanStepItem[_operationCount];
43+
_nextScanStepSlot = 0;
3444

35-
for (int i = 0; i < sanitized.ConsumedPreComputedCacheSlots; i++)
45+
for (int i = 0; i < sanitized.ConsumedPlaceholderCacheSlots; i++)
3646
{
37-
_preComputedCache[i] = new PreComputedCacheItem()
47+
_placeholderCache[i] = new PlaceholderCacheItem()
3848
{
3949
ComputedValue = options.DefaultNullValue,
4050
IsVariable = false,
@@ -50,60 +60,103 @@ public ExpressionState(ExpressionOptions options, int preAllocation)
5060
_options = options;
5161
}
5262

53-
#region Pre-Parsed Cache Management.
63+
#region Scan Step Cache Management.
5464

55-
public int ConsumeNextPlaceholderCacheSlot()
65+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
66+
public bool TryGetScanStep([NotNullWhen(true)] out ScanStepItem value)
5667
{
57-
return _nextPlaceholderCacheSlot++;
68+
if (_nextScanStepSlot < _consumedScanStepSlots)
69+
{
70+
value = _scanStep[_nextScanStepSlot];
71+
_nextScanStepSlot++;
72+
return value.IsValid;
73+
}
74+
_nextScanStepSlot++;
75+
value = default;
76+
return false;
5877
}
5978

6079
[MethodImpl(MethodImplOptions.AggressiveInlining)]
61-
public bool TryGetPlaceholderCache(int slot, [NotNullWhen(true)] out PlaceholderCacheItem value)
80+
public void StoreScanStep(ScanStepItem value)
6281
{
63-
if (slot < _placeholderCache.Length)
82+
if (_consumedScanStepSlots >= _scanStep.Length) //Resize the cache if needed.
6483
{
65-
var cached = _placeholderCache[slot];
66-
if (cached != null)
67-
{
68-
value = cached.Value;
69-
return true;
70-
}
84+
Array.Resize(ref _scanStep, (_scanStep.Length + 1) * 2);
85+
}
86+
value.IsValid = true;
87+
_scanStep[_consumedScanStepSlots++] = value;
88+
}
89+
90+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
91+
public void IncrementScanStep()
92+
{
93+
if (_consumedScanStepSlots >= _scanStep.Length) //Resize the cache if needed.
94+
{
95+
Array.Resize(ref _scanStep, (_scanStep.Length + 1) * 2);
96+
}
97+
_scanStep[_consumedScanStepSlots++] = new ScanStepItem() { IsValid = false };
98+
}
99+
100+
#endregion
101+
102+
#region Computed Step Cache Management.
103+
104+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
105+
public bool TryGetComputedStep([NotNullWhen(true)] out ComputedStepItem value)
106+
{
107+
if (_nextComputedStepSlot < _consumedComputedStepSlots)
108+
{
109+
value = _computedStep[_nextComputedStepSlot];
110+
_nextComputedStepSlot++;
111+
return value.IsValid;
71112
}
113+
_nextComputedStepSlot++;
72114
value = default;
73115
return false;
74116
}
75117

76118
[MethodImpl(MethodImplOptions.AggressiveInlining)]
77-
public void StorePlaceholderCache(int slot, PlaceholderCacheItem value)
119+
public void StoreComputedStep(ComputedStepItem value)
78120
{
79-
if (slot >= _placeholderCache.Length) //Resize the cache if needed.
121+
if (_consumedComputedStepSlots >= _computedStep.Length) //Resize the cache if needed.
80122
{
81-
Array.Resize(ref _placeholderCache, (_placeholderCache.Length + 1) * 2);
123+
Array.Resize(ref _computedStep, (_computedStep.Length + 1) * 2);
124+
}
125+
value.IsValid = true;
126+
_computedStep[_consumedComputedStepSlots++] = value;
127+
}
128+
129+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
130+
public void IncrementComputedStep()
131+
{
132+
if (_consumedComputedStepSlots >= _computedStep.Length) //Resize the cache if needed.
133+
{
134+
Array.Resize(ref _computedStep, (_computedStep.Length + 1) * 2);
82135
}
83-
_placeholderCache[slot] = value;
136+
_computedStep[_consumedComputedStepSlots++] = new ComputedStepItem() { IsValid = false };
84137
}
85138

86139
#endregion
87140

88141
#region Pre-Computed Cache Management.
89142

90-
public int ConsumeNextPreComputedCacheSlot(out string cacheKey)
143+
public int ConsumeNextPlaceholderCacheSlot(out string cacheKey)
91144
{
92-
int cacheSlot = _nextPreComputedCacheSlot++;
145+
int cacheSlot = _nextPlaceholderCacheSlot++;
93146
cacheKey = $"${cacheSlot}$";
94147
return cacheSlot;
95148
}
96149

97-
public string StorePreComputedCacheItem(double? value, bool isVariable = false)
150+
public string StorePlaceholderCacheItem(double? value, bool isVariable = false)
98151
{
99-
var cacheSlot = ConsumeNextPreComputedCacheSlot(out var cacheKey);
152+
var cacheSlot = ConsumeNextPlaceholderCacheSlot(out var cacheKey);
100153

101-
if (cacheSlot >= _preComputedCache.Length) //Resize the cache if needed.
154+
if (cacheSlot >= _placeholderCache.Length) //Resize the cache if needed.
102155
{
103-
Array.Resize(ref _preComputedCache, (_preComputedCache.Length + 1) * 2);
156+
Array.Resize(ref _placeholderCache, (_placeholderCache.Length + 1) * 2);
104157
}
105158

106-
_preComputedCache[cacheSlot] = new PreComputedCacheItem()
159+
_placeholderCache[cacheSlot] = new PlaceholderCacheItem()
107160
{
108161
IsVariable = isVariable,
109162
ComputedValue = value
@@ -112,67 +165,88 @@ public string StorePreComputedCacheItem(double? value, bool isVariable = false)
112165
return cacheKey;
113166
}
114167

115-
public PreComputedCacheItem GetPreComputedCacheItem(ReadOnlySpan<char> span)
168+
public PlaceholderCacheItem GetPlaceholderCacheItem(ReadOnlySpan<char> span)
116169
{
117170
switch (span.Length)
118171
{
119172
case 1:
120-
return _preComputedCache[span[0] - '0'];
173+
return _placeholderCache[span[0] - '0'];
121174
default:
122175
int index = 0;
123176
for (int i = 0; i < span.Length; i++)
124177
index = index * 10 + (span[i] - '0');
125-
return _preComputedCache[index];
178+
return _placeholderCache[index];
126179
}
127180
}
128181

129182
#endregion
130183

131-
public void HydrateTemplateParsedCache(int expressionHash)
184+
public void HydrateTemplateCache(int expressionHash)
132185
{
133-
if (!_isPlaceholderCacheHydrated)
186+
if (!_isTemplateCacheHydrated)
134187
{
135188
lock (this)
136189
{
137-
if (!_isPlaceholderCacheHydrated)
190+
if (!_isTemplateCacheHydrated)
138191
{
139192
if (Utility.PersistentCaches.TryGetValue(expressionHash, out CachedState? entry) && entry != null)
140193
{
141-
entry.State.HydratePlaceholderCache(_placeholderCache);
194+
entry.State.HydrateComputedStepCache(_computedStep, _consumedComputedStepSlots);
195+
entry.State.HydrateScanStepCache(_scanStep, _consumedScanStepSlots);
142196
}
143-
_isPlaceholderCacheHydrated = true;
197+
_isTemplateCacheHydrated = true;
144198
}
145199
}
146200
}
147201
}
148202

149-
private void HydratePlaceholderCache(PlaceholderCacheItem?[] populatedCache)
203+
private void HydrateScanStepCache(ScanStepItem[] populatedCache, int consumedScanStepSlots)
150204
{
151-
Interlocked.Exchange(ref _placeholderCache, populatedCache);
205+
_consumedScanStepSlots = consumedScanStepSlots;
206+
Interlocked.Exchange(ref _scanStep, populatedCache);
207+
}
208+
209+
private void HydrateComputedStepCache(ComputedStepItem[] populatedCache, int consumedComputedStepSlots)
210+
{
211+
_consumedComputedStepSlots = consumedComputedStepSlots;
212+
Interlocked.Exchange(ref _computedStep, populatedCache);
152213
}
153214

154215
public void Reset(Sanitized sanitized)
155216
{
156217
WorkingText = sanitized.Text;
157-
_nextPreComputedCacheSlot = sanitized.ConsumedPreComputedCacheSlots;
158-
_nextPlaceholderCacheSlot = 0;
218+
_nextPlaceholderCacheSlot = sanitized.ConsumedPlaceholderCacheSlots;
219+
_nextComputedStepSlot = 0;
159220
}
160221

161-
public ExpressionState Clone()
222+
public ExpressionState Clone(Sanitized sanitized)
162223
{
163224
var clone = new ExpressionState(_options, WorkingText.Length * 2)
164225
{
165226
WorkingText = WorkingText,
166227
_operationCount = _operationCount,
167-
_nextPreComputedCacheSlot = _nextPreComputedCacheSlot,
168-
_preComputedCache = new PreComputedCacheItem[_preComputedCache.Length],
169-
_placeholderCache = new PlaceholderCacheItem?[_placeholderCache.Length],
170-
_nextPlaceholderCacheSlot = 0,
171-
_isPlaceholderCacheHydrated = _isPlaceholderCacheHydrated
228+
_nextPlaceholderCacheSlot = _nextPlaceholderCacheSlot,
229+
_placeholderCache = new PlaceholderCacheItem[_placeholderCache.Length],
230+
_isTemplateCacheHydrated = _isTemplateCacheHydrated,
231+
232+
_computedStep = new ComputedStepItem[_computedStep.Length],
233+
_nextComputedStepSlot = 0,
234+
_consumedComputedStepSlots = _consumedComputedStepSlots,
235+
236+
_scanStep = new ScanStepItem[_scanStep.Length],
237+
_nextScanStepSlot = 0,
238+
_consumedScanStepSlots = _consumedScanStepSlots
239+
172240
};
173241

174-
Array.Copy(_preComputedCache, clone._preComputedCache, _preComputedCache.Length); //Copy any pre-computed NULLs.
175-
Array.Copy(_placeholderCache, clone._placeholderCache, _placeholderCache.Length);
242+
for (int i = 0; i < sanitized.ConsumedPlaceholderCacheSlots; i++)
243+
{
244+
clone._placeholderCache[i] = _placeholderCache[i]; //Copy any pre-defined NULLs.
245+
}
246+
247+
//Array.Copy(_placeholderCache, clone._placeholderCache, _placeholderCache.Length); //Copy any pre-computed NULLs.
248+
Array.Copy(_computedStep, clone._computedStep, _computedStep.Length);
249+
Array.Copy(_scanStep, clone._scanStep, _scanStep.Length);
176250

177251
return clone;
178252
}
@@ -184,8 +258,8 @@ public void ApplyParameters(Sanitized sanitized, Dictionary<string, double?> def
184258
{
185259
if (definedParameters.TryGetValue(variable, out var value))
186260
{
187-
var cacheSlot = ConsumeNextPreComputedCacheSlot(out var cacheKey);
188-
_preComputedCache[cacheSlot] = new PreComputedCacheItem()
261+
var cacheSlot = ConsumeNextPlaceholderCacheSlot(out var cacheKey);
262+
_placeholderCache[cacheSlot] = new PlaceholderCacheItem()
189263
{
190264
ComputedValue = value ?? _options.DefaultNullValue,
191265
IsVariable = true

NTDLS.ExpressionParser/Sanitized.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
internal class Sanitized
44
{
55
public int OperationCount { get; set; }
6-
public int ConsumedPreComputedCacheSlots { get; set; }
6+
public int ConsumedPlaceholderCacheSlots { get; set; }
77
public string Text { get; set; } = string.Empty;
88
internal HashSet<string> DiscoveredVariables { get; private set; } = new();
99
internal HashSet<string> DiscoveredFunctions { get; private set; } = new();

NTDLS.ExpressionParser/Sanitizer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public static Sanitized Process(string expressionText, ExpressionOptions options
3333
sanitized.OperationCount++;
3434
}
3535

36-
sanitized.ConsumedPreComputedCacheSlots = sanitized.OperationCount;
36+
sanitized.ConsumedPlaceholderCacheSlots = sanitized.OperationCount;
3737

3838
var expressionSpan = expressionText.AsSpan();
3939

0 commit comments

Comments
 (0)