Skip to content

Commit feff96d

Browse files
committed
Cache fixes.
1 parent bdcf7a0 commit feff96d

File tree

7 files changed

+201
-102
lines changed

7 files changed

+201
-102
lines changed

NTDLS.ExpressionParser/Expression.cs

Lines changed: 15 additions & 11 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(cached.Sanitized);
51+
State = cached.State.Clone();
5252
}
5353
else
5454
{
@@ -69,24 +69,26 @@ public Expression(string text, ExpressionOptions? options = null)
6969
State.Reset(Sanitized);
7070
State.ApplyParameters(Sanitized, _definedParameters);
7171

72+
bool isCacheable = ExpressionFunctions.Count == 0;
73+
7274
bool isComplete;
7375
do
7476
{
7577
//Get a sub-expression from the whole expression.
7678
isComplete = AcquireSubexpression(out int startIndex, out int endIndex, out var subExpression);
7779
//Compute the sub-expression.
78-
var resultString = subExpression.Compute();
80+
var resultString = subExpression.Compute(isCacheable);
7981
//Replace the sub-expression in the whole expression with the result from the sub-expression computation.
8082
State.WorkingText = ReplaceRange(State.WorkingText, startIndex, endIndex, resultString);
8183
} while (!isComplete);
8284

8385
if (State.WorkingText[0] == '$')
8486
{
85-
//State.HydrateTemplateParsedCache(_expressionHash);
86-
return State.GetPlaceholderCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
87+
State.HydrateTemplateParsedCache(_expressionHash);
88+
return State.GetPreComputedCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
8789
}
8890

89-
//State.HydrateTemplateParsedCache(_expressionHash);
91+
State.HydrateTemplateParsedCache(_expressionHash);
9092
return StringToDouble(State.WorkingText);
9193
}
9294

@@ -103,6 +105,8 @@ public Expression(string text, ExpressionOptions? options = null)
103105

104106
work.AppendLine("{");
105107

108+
bool isCacheable = ExpressionFunctions.Count == 0;
109+
106110
bool isComplete;
107111
do
108112
{
@@ -113,7 +117,7 @@ public Expression(string text, ExpressionOptions? options = null)
113117
work.Append(" " + friendlySubExpression);
114118

115119
//Compute the sub-expression.
116-
var resultString = subExpression.Compute();
120+
var resultString = subExpression.Compute(isCacheable);
117121

118122
work.AppendLine($" = {SwapInCacheValues(resultString)}");
119123

@@ -126,13 +130,13 @@ public Expression(string text, ExpressionOptions? options = null)
126130
if (State.WorkingText[0] == '$')
127131
{
128132
showWork = work.ToString();
129-
//State.HydrateTemplateParsedCache(_expressionHash);
130-
return State.GetPlaceholderCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
133+
State.HydrateTemplateParsedCache(_expressionHash);
134+
return State.GetPreComputedCacheItem(State.WorkingText.AsSpan()[1..^1]).ComputedValue;
131135
}
132136

133137
showWork = work.ToString();
134138

135-
//State.HydrateTemplateParsedCache(_expressionHash);
139+
State.HydrateTemplateParsedCache(_expressionHash);
136140
return StringToDouble(State.WorkingText);
137141
}
138142

@@ -270,7 +274,7 @@ private string SwapInCacheValues(string text)
270274
if (begIndex >= 0 && endIndex > begIndex)
271275
{
272276
var cacheKey = copy.Substring(begIndex + 1, (endIndex - begIndex) - 1);
273-
copy = copy.Replace($"${cacheKey}$", State.GetPlaceholderCacheItem(cacheKey).ComputedValue?.ToString(_precisionFormat) ?? "null");
277+
copy = copy.Replace($"${cacheKey}$", State.GetPreComputedCacheItem(cacheKey).ComputedValue?.ToString(_precisionFormat) ?? "null");
274278
}
275279
else
276280
{
@@ -358,7 +362,7 @@ internal bool AcquireSubexpression(out int outStartIndex, out int outEndIndex, o
358362
}
359363
else if (span[0] == '$')
360364
{
361-
return State.GetPlaceholderCacheItem(span[1..^1]).ComputedValue;
365+
return State.GetPreComputedCacheItem(span[1..^1]).ComputedValue;
362366
}
363367

364368
if (Options.UseFastFloatingPointParser)

NTDLS.ExpressionParser/ExpressionState.cs

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

15-
private int _nextPositionStepCacheSlot = 0;
16-
private PlaceholderCacheItem[] _placeholderCache = [];
1715
private int _nextPlaceholderCacheSlot = 0;
16+
private PreComputedCacheItem[] _preComputedCache = [];
17+
private int _nextPreComputedCacheSlot = 0;
1818
private int _operationCount = 0;
19-
private PositionStepCacheItem[] _positionStepCache = [];
19+
private PlaceholderCacheItem?[] _placeholderCache = [];
2020
private readonly ExpressionOptions _options;
21-
private bool _isPositionStepCacheHydrated = false;
21+
private bool _isPlaceholderCacheHydrated = false;
2222

2323
public ExpressionState(Sanitized sanitized, ExpressionOptions options)
2424
{
@@ -27,14 +27,14 @@ public ExpressionState(Sanitized sanitized, ExpressionOptions options)
2727

2828
WorkingText = sanitized.Text;
2929
_operationCount = sanitized.OperationCount;
30-
_nextPlaceholderCacheSlot = sanitized.ConsumedPlaceholderCacheSlots;
31-
_placeholderCache = new PlaceholderCacheItem[_operationCount + 10];
32-
_positionStepCache = new PositionStepCacheItem[_operationCount + 10];
33-
_nextPositionStepCacheSlot = 0;
30+
_nextPreComputedCacheSlot = sanitized.ConsumedPreComputedCacheSlots;
31+
_preComputedCache = new PreComputedCacheItem[sanitized.OperationCount];
32+
_placeholderCache = new PlaceholderCacheItem?[_operationCount];
33+
_nextPlaceholderCacheSlot = 0;
3434

35-
for (int i = 0; i < sanitized.ConsumedPlaceholderCacheSlots; i++)
35+
for (int i = 0; i < sanitized.ConsumedPreComputedCacheSlots; i++)
3636
{
37-
_placeholderCache[i] = new PlaceholderCacheItem()
37+
_preComputedCache[i] = new PreComputedCacheItem()
3838
{
3939
ComputedValue = options.DefaultNullValue,
4040
IsVariable = false,
@@ -50,53 +50,60 @@ public ExpressionState(ExpressionOptions options, int preAllocation)
5050
_options = options;
5151
}
5252

53-
#region Position Step Cache Management.
53+
#region Pre-Parsed Cache Management.
5454

55-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
56-
public bool TryGetPositionStepCache([NotNullWhen(true)] out PositionStepCacheItem value, out int slot)
55+
public int ConsumeNextPlaceholderCacheSlot()
5756
{
58-
slot = _nextPositionStepCacheSlot++;
57+
return _nextPlaceholderCacheSlot++;
58+
}
5959

60-
if (slot < _nextPositionStepCacheSlot - 1)
60+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
61+
public bool TryGetPlaceholderCache(int slot, [NotNullWhen(true)] out PlaceholderCacheItem value)
62+
{
63+
if (slot < _placeholderCache.Length)
6164
{
62-
value = _positionStepCache[slot];
63-
return true;
65+
var cached = _placeholderCache[slot];
66+
if (cached != null)
67+
{
68+
value = cached.Value;
69+
return true;
70+
}
6471
}
6572
value = default;
6673
return false;
6774
}
6875

6976
[MethodImpl(MethodImplOptions.AggressiveInlining)]
70-
public void StorePositionStepCache(int slot, PositionStepCacheItem value)
77+
public void StorePlaceholderCache(int slot, PlaceholderCacheItem value)
7178
{
72-
if (slot >= _positionStepCache.Length) //Resize the cache if needed.
79+
if (slot >= _placeholderCache.Length) //Resize the cache if needed.
7380
{
74-
Array.Resize(ref _positionStepCache, (_positionStepCache.Length + 1) * 2);
81+
Array.Resize(ref _placeholderCache, (_placeholderCache.Length + 1) * 2);
7582
}
76-
_positionStepCache[slot] = value;
83+
_placeholderCache[slot] = value;
7784
}
7885

7986
#endregion
8087

81-
#region Placeholder Cache Management.
88+
#region Pre-Computed Cache Management.
8289

83-
public int ConsumeNextPlaceholderCacheSlot(out string cacheKey)
90+
public int ConsumeNextPreComputedCacheSlot(out string cacheKey)
8491
{
85-
int cacheSlot = _nextPlaceholderCacheSlot++;
92+
int cacheSlot = _nextPreComputedCacheSlot++;
8693
cacheKey = $"${cacheSlot}$";
8794
return cacheSlot;
8895
}
8996

90-
public string StorePlaceholderCacheItem(double? value, bool isVariable = false)
97+
public string StorePreComputedCacheItem(double? value, bool isVariable = false)
9198
{
92-
var cacheSlot = ConsumeNextPlaceholderCacheSlot(out var cacheKey);
99+
var cacheSlot = ConsumeNextPreComputedCacheSlot(out var cacheKey);
93100

94-
if (cacheSlot >= _placeholderCache.Length) //Resize the cache if needed.
101+
if (cacheSlot >= _preComputedCache.Length) //Resize the cache if needed.
95102
{
96-
Array.Resize(ref _placeholderCache, (_placeholderCache.Length + 1) * 2);
103+
Array.Resize(ref _preComputedCache, (_preComputedCache.Length + 1) * 2);
97104
}
98105

99-
_placeholderCache[cacheSlot] = new PlaceholderCacheItem()
106+
_preComputedCache[cacheSlot] = new PreComputedCacheItem()
100107
{
101108
IsVariable = isVariable,
102109
ComputedValue = value
@@ -105,74 +112,67 @@ public string StorePlaceholderCacheItem(double? value, bool isVariable = false)
105112
return cacheKey;
106113
}
107114

108-
public PlaceholderCacheItem GetPlaceholderCacheItem(ReadOnlySpan<char> span)
115+
public PreComputedCacheItem GetPreComputedCacheItem(ReadOnlySpan<char> span)
109116
{
110117
switch (span.Length)
111118
{
112119
case 1:
113-
return _placeholderCache[span[0] - '0'];
120+
return _preComputedCache[span[0] - '0'];
114121
default:
115122
int index = 0;
116123
for (int i = 0; i < span.Length; i++)
117124
index = index * 10 + (span[i] - '0');
118-
return _placeholderCache[index];
125+
return _preComputedCache[index];
119126
}
120127
}
121128

122129
#endregion
123130

124-
/*
125131
public void HydrateTemplateParsedCache(int expressionHash)
126132
{
127-
if (!_isPositionStepCacheHydrated)
133+
if (!_isPlaceholderCacheHydrated)
128134
{
129135
lock (this)
130136
{
131-
if (!_isPositionStepCacheHydrated)
137+
if (!_isPlaceholderCacheHydrated)
132138
{
133139
if (Utility.PersistentCaches.TryGetValue(expressionHash, out CachedState? entry) && entry != null)
134140
{
135-
entry.State.HydratePositionStepCache(_positionStepCache);
141+
entry.State.HydratePlaceholderCache(_placeholderCache);
136142
}
137-
_isPositionStepCacheHydrated = true;
143+
_isPlaceholderCacheHydrated = true;
138144
}
139145
}
140146
}
141147
}
142148

143-
private void HydratePositionStepCache(PositionStepCacheItem[] populatedCache)
149+
private void HydratePlaceholderCache(PlaceholderCacheItem?[] populatedCache)
144150
{
145-
Interlocked.Exchange(ref _positionStepCache, populatedCache);
151+
Interlocked.Exchange(ref _placeholderCache, populatedCache);
146152
}
147-
*/
148153

149154
public void Reset(Sanitized sanitized)
150155
{
151156
WorkingText = sanitized.Text;
152-
_nextPlaceholderCacheSlot = sanitized.ConsumedPlaceholderCacheSlots;
153-
_nextPositionStepCacheSlot = 0;
157+
_nextPreComputedCacheSlot = sanitized.ConsumedPreComputedCacheSlots;
158+
_nextPlaceholderCacheSlot = 0;
154159
}
155160

156-
public ExpressionState Clone(Sanitized sanitized)
161+
public ExpressionState Clone()
157162
{
158163
var clone = new ExpressionState(_options, WorkingText.Length * 2)
159164
{
160165
WorkingText = WorkingText,
161166
_operationCount = _operationCount,
162-
_nextPlaceholderCacheSlot = _nextPlaceholderCacheSlot,
163-
_placeholderCache = new PlaceholderCacheItem[_placeholderCache.Length],
164-
_positionStepCache = new PositionStepCacheItem[_positionStepCache.Length],
165-
_nextPositionStepCacheSlot = 0,
166-
_isPositionStepCacheHydrated = _isPositionStepCacheHydrated
167+
_nextPreComputedCacheSlot = _nextPreComputedCacheSlot,
168+
_preComputedCache = new PreComputedCacheItem[_preComputedCache.Length],
169+
_placeholderCache = new PlaceholderCacheItem?[_placeholderCache.Length],
170+
_nextPlaceholderCacheSlot = 0,
171+
_isPlaceholderCacheHydrated = _isPlaceholderCacheHydrated
167172
};
168173

169-
for(int i = 0; i < sanitized.ConsumedPlaceholderCacheSlots; i++)
170-
{
171-
clone._placeholderCache[i] = _placeholderCache[i]; //Copy any pre-defined NULLs.
172-
}
173-
174-
//Array.Copy(_placeholderCache, clone._placeholderCache, _placeholderCache.Length); //Copy any pre-computed NULLs.
175-
Array.Copy(_positionStepCache, clone._positionStepCache, _positionStepCache.Length);
174+
Array.Copy(_preComputedCache, clone._preComputedCache, _preComputedCache.Length); //Copy any pre-computed NULLs.
175+
Array.Copy(_placeholderCache, clone._placeholderCache, _placeholderCache.Length);
176176

177177
return clone;
178178
}
@@ -184,8 +184,8 @@ public void ApplyParameters(Sanitized sanitized, Dictionary<string, double?> def
184184
{
185185
if (definedParameters.TryGetValue(variable, out var value))
186186
{
187-
var cacheSlot = ConsumeNextPlaceholderCacheSlot(out var cacheKey);
188-
_placeholderCache[cacheSlot] = new PlaceholderCacheItem()
187+
var cacheSlot = ConsumeNextPreComputedCacheSlot(out var cacheKey);
188+
_preComputedCache[cacheSlot] = new PreComputedCacheItem()
189189
{
190190
ComputedValue = value ?? _options.DefaultNullValue,
191191
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 ConsumedPlaceholderCacheSlots { get; set; }
6+
public int ConsumedPreComputedCacheSlots { 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.ConsumedPlaceholderCacheSlots = sanitized.OperationCount;
36+
sanitized.ConsumedPreComputedCacheSlots = sanitized.OperationCount;
3737

3838
var expressionSpan = expressionText.AsSpan();
3939

0 commit comments

Comments
 (0)