Skip to content

Commit 55219cc

Browse files
jderegclaude
andcommitted
Performance: Use local variables in FastReader.readUntil() hot loops
Copy member fields to locals before loops and write back after. This allows the JVM to keep loop variables in CPU registers rather than repeatedly loading/storing from heap memory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 385b5ef commit 55219cc

2 files changed

Lines changed: 26 additions & 7 deletions

File tree

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* Reads characters into destination buffer until one of two delimiters is found
1111
* Delimiter character is left unconsumed for subsequent read
1212
* Enables bulk string parsing optimization in json-io's JsonParser
13+
* Uses local variables instead of member fields in hot loops to enable register allocation
1314

1415
#### 4.89.0 - 2026-01-31
1516
* **PERFORMANCE**: `FastReader.getLastSnippet()` now returns bounded 200-char context

src/main/java/com/cedarsoftware/util/FastReader.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,39 +167,57 @@ public int readUntil(char[] dest, int off, int maxLen, char delim1, char delim2)
167167

168168
int totalRead = 0;
169169

170+
// Copy member variables to locals for faster loop access (avoids repeated field loads)
171+
final char[] localPushbackBuffer = pushbackBuffer;
172+
final int localPushbackBufferSize = pushbackBufferSize;
173+
int localPushbackPosition = pushbackPosition;
174+
final char[] localBuf = buf;
175+
int localPosition = position;
176+
int localLimit = limit;
177+
170178
// First, drain any pushback buffer
171-
while (pushbackPosition < pushbackBufferSize && totalRead < maxLen) {
172-
char c = pushbackBuffer[pushbackPosition];
179+
while (localPushbackPosition < localPushbackBufferSize && totalRead < maxLen) {
180+
char c = localPushbackBuffer[localPushbackPosition];
173181
if (c == delim1 || c == delim2) {
174182
// Found delimiter in pushback - don't consume it
183+
pushbackPosition = localPushbackPosition; // Write back before return
175184
return totalRead > 0 ? totalRead : 0;
176185
}
177186
dest[off++] = c;
178-
pushbackPosition++;
187+
localPushbackPosition++;
179188
totalRead++;
180189
}
190+
pushbackPosition = localPushbackPosition; // Write back after pushback loop
181191

182192
// Now read from main buffer
183193
while (totalRead < maxLen) {
194+
// Write back position before fill() since fill() may modify position/limit
195+
position = localPosition;
184196
fill();
185-
if (limit == -1) {
197+
// Re-read after fill() since it may have changed position and limit
198+
localPosition = position;
199+
localLimit = limit;
200+
201+
if (localLimit == -1) {
186202
// EOF reached
187203
return totalRead > 0 ? totalRead : -1;
188204
}
189205

190206
// Scan current buffer for delimiters
191-
while (position < limit && totalRead < maxLen) {
192-
char c = buf[position];
207+
while (localPosition < localLimit && totalRead < maxLen) {
208+
char c = localBuf[localPosition];
193209
if (c == delim1 || c == delim2) {
194210
// Found delimiter - don't consume it
211+
position = localPosition; // Write back before return
195212
return totalRead;
196213
}
197214
dest[off++] = c;
198-
position++;
215+
localPosition++;
199216
totalRead++;
200217
}
201218
}
202219

220+
position = localPosition; // Write back after main loop
203221
return totalRead;
204222
}
205223

0 commit comments

Comments
 (0)