Skip to content

Commit db1dbe1

Browse files
jderegclaude
andcommitted
Optimize FastReader.readUntil() with scan+arraycopy pattern
Split the inner loop into a read-only delimiter scan followed by a bulk System.arraycopy, allowing the JIT to optimize the tight scan loop independently from the memory write. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bf7f28a commit db1dbe1

2 files changed

Lines changed: 19 additions & 15 deletions

File tree

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<groupId>com.cedarsoftware</groupId>
66
<artifactId>java-util</artifactId>
77
<packaging>bundle</packaging>
8-
<version>4.97.0-SNAPSHOT</version>
8+
<version>4.97.0</version>
99
<description>Java Utilities</description>
1010
<url>https://github.com/jdereg/java-util</url>
1111

@@ -32,7 +32,7 @@
3232
<version.junit-jupiter-params>5.14.2</version.junit-jupiter-params>
3333
<version.mockito-junit-jupiter>4.11.0</version.mockito-junit-jupiter> <!-- can't update until they have JDK support -->
3434
<version.assertj-core>3.27.7</version.assertj-core>
35-
<version.json-io>4.94.0</version.json-io>
35+
<version.json-io>4.96.0</version.json-io>
3636
<version.agrona>1.22.0</version.agrona> <!-- do not increase past 1.22.0 until abandoning JDK 8 support, class file format 52 -->
3737
<version.commons-collections4>4.5.0</version.commons-collections4>
3838
<version.guava>33.5.0-jre</version.guava>

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

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -220,22 +220,26 @@ public int readUntil(final char[] dest, int off, int maxLen, char delim1, char d
220220
return totalRead > 0 ? totalRead : -1;
221221
}
222222

223-
// Single-pass loop with position localized to avoid per-char field write
223+
// Scan for delimiter in a tight loop (reads only, no writes),
224+
// then bulk-copy with System.arraycopy (JVM intrinsic).
224225
int pos = position;
225226
int end = pos + Math.min(limit - pos, maxLen - totalRead);
226-
while (pos < end) {
227-
char c = locBuf[pos];
228-
if (c == delim1 || c == delim2) {
229-
// Found delimiter — don't consume it
230-
totalRead += pos - position;
231-
position = pos;
232-
return totalRead;
233-
}
234-
dest[off++] = c;
235-
pos++;
227+
int scanPos = pos;
228+
while (scanPos < end && locBuf[scanPos] != delim1 && locBuf[scanPos] != delim2) {
229+
scanPos++;
230+
}
231+
int copyLen = scanPos - pos;
232+
if (copyLen > 0) {
233+
System.arraycopy(locBuf, pos, dest, off, copyLen);
234+
off += copyLen;
235+
totalRead += copyLen;
236+
}
237+
if (scanPos < end) {
238+
// Found delimiter — don't consume it
239+
position = scanPos;
240+
return totalRead;
236241
}
237-
totalRead += pos - position;
238-
position = pos;
242+
position = scanPos;
239243
}
240244

241245
return totalRead;

0 commit comments

Comments
 (0)