From 114c0496f80f78a166a36607e320c35d7131aa5d Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Wed, 4 Feb 2026 07:21:38 -0800 Subject: [PATCH] Improve readString performance --- .../hollow/core/memory/encoding/VarInt.java | 35 +++++++++++++++---- .../HollowObjectTypeReadStateShard.java | 2 -- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/hollow/src/main/java/com/netflix/hollow/core/memory/encoding/VarInt.java b/hollow/src/main/java/com/netflix/hollow/core/memory/encoding/VarInt.java index 4077bb4e4..8d7be06ec 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/memory/encoding/VarInt.java +++ b/hollow/src/main/java/com/netflix/hollow/core/memory/encoding/VarInt.java @@ -243,7 +243,7 @@ public static int readVInt(byte[] arr, int position) { * @param arr the byte data to read from * @param position the position in the byte data to read from * @param length the number of bytes that should be read - * @param output an array where outputs will be placed, which should be at least {@code length} long and zero. + * @param output an array where outputs will be placed, which should be at least {@code length} long. * @return the number of values written */ public static int readVIntsInto(ByteData arr, long position, int length, int[] output) { @@ -256,12 +256,24 @@ public static int readVIntsInto(ByteData arr, long position, int length, int[] o output[i] = b; } + // Second loop handles multi-byte encoded values using branchless logic int count = i; + int accumulator = 0; for(; i < length; i++) { int b = arr.get(position + i); - output[count] = (output[count] << 7) | (b & 0x7f); - count += (~b >> 7) & 0x1; + // Accumulate the next 7 bits into the value + accumulator = (accumulator << 7) | (b & 0x7f); + + // Check if this byte completes a value (continuation bit not set) + int isComplete = (~b >> 7) & 0x1; // 1 if complete, 0 if continuing + output[count] = accumulator; // Write current accumulator (may overwrite same position) + count += isComplete; // Advance output position only when value is complete + + // Clear accumulator for next value using sign extension trick: + // - If b has continuation bit (b negative): b >> 7 = 0xFFFFFFFF, keeps accumulator + // - If b completes value (b positive): b >> 7 = 0, clears accumulator + accumulator &= (b >> 7); } return count; @@ -272,7 +284,7 @@ public static int readVIntsInto(ByteData arr, long position, int length, int[] o * @param arr the byte data to read from * @param position the position in the byte data to read from * @param length the number of bytes that should be read - * @param output an array where outputs will be placed, which should be at least {@code length} long and zero. + * @param output an array where outputs will be placed, which should be at least {@code length} long. * @return the number of values written */ public static int readVIntsInto(ByteData arr, long position, int length, char[] output) { @@ -286,11 +298,22 @@ public static int readVIntsInto(ByteData arr, long position, int length, char[] } int count = i; + int accumulator = 0; for(; i < length; i++) { int b = arr.get(position + i); - output[count] = (char) ((output[count] << 7) | (b & 0x7f)); - count += (~b >> 7) & 0x1; + // Accumulate the next 7 bits into the value + accumulator = (accumulator << 7) | (b & 0x7f); + + // Check if this byte completes a value (continuation bit not set) + int isComplete = (~b >> 7) & 0x1; // 1 if complete, 0 if continuing + output[count] = (char) accumulator; // Write current accumulator (may overwrite same position) + count += isComplete; // Advance output position only when value is complete + + // Clear accumulator for next value using sign extension trick: + // - If b has continuation bit (b negative): b >> 7 = 0xFFFFFFFF, keeps accumulator + // - If b completes value (b positive): b >> 7 = 0, clears accumulator + accumulator &= (b >> 7); } return count; diff --git a/hollow/src/main/java/com/netflix/hollow/core/read/engine/object/HollowObjectTypeReadStateShard.java b/hollow/src/main/java/com/netflix/hollow/core/read/engine/object/HollowObjectTypeReadStateShard.java index fe3745f45..ea66137ef 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/read/engine/object/HollowObjectTypeReadStateShard.java +++ b/hollow/src/main/java/com/netflix/hollow/core/read/engine/object/HollowObjectTypeReadStateShard.java @@ -170,8 +170,6 @@ private String readString(ByteData data, long position, int length) { char[] chararr = HollowObjectTypeReadStateShard.chararr.get(); if (length > chararr.length) { chararr = new char[length]; - } else { - Arrays.fill(chararr, 0, length, '\0'); } int count = VarInt.readVIntsInto(data, position, length, chararr);