From 7a10ec931a4c1d0a3f7767db5915ba24395de676 Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:54:14 +0000 Subject: [PATCH] Optimize LuaUnpacker.getString MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A small per-instance direct-mapped string cache was added to LuaUnpacker (256-entry power-of-two slot arrays), producing a ~5% end-to-end speedup (426 µs → 405 µs) by avoiding repeated LuaString allocations for recently seen values. The key insight is that many unpacked strings are repeated in hot paths, so computing a cheap hash/index and doing a single equals check is much cheaper than always calling LuaString.valueOf and allocating a new object, which reduces object churn and GC pressure. Trade-offs are a modest per-Unpacker memory cost (two small reference arrays) and the possibility of slot thrashing under extremely high-uniqueness workloads where the direct-mapped cache yields little benefit; the cache is instance-local and lock-free to avoid synchronization overhead. Null inputs are still passed through to LuaString.valueOf to retain the original exception/return semantics. --- .../com/aerospike/client/lua/LuaUnpacker.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/client/src/com/aerospike/client/lua/LuaUnpacker.java b/client/src/com/aerospike/client/lua/LuaUnpacker.java index b4c8b37cb..7171a799f 100644 --- a/client/src/com/aerospike/client/lua/LuaUnpacker.java +++ b/client/src/com/aerospike/client/lua/LuaUnpacker.java @@ -30,6 +30,10 @@ public class LuaUnpacker extends Unpacker { private LuaInstance instance; + private static final int STRING_CACHE_SIZE = 256; + private static final int STRING_CACHE_MASK = STRING_CACHE_SIZE - 1; + private final String[] stringCacheKeys = new String[STRING_CACHE_SIZE]; + private final LuaString[] stringCacheValues = new LuaString[STRING_CACHE_SIZE]; public LuaUnpacker(LuaInstance instance, byte[] buffer, int offset, int length) { super(buffer, offset, length); @@ -53,7 +57,26 @@ protected LuaBytes getBlob(byte[] value) { @Override protected LuaString getString(String value) { - return LuaString.valueOf(value); + // Preserve original behavior for null values (delegate directly), + // so any exceptions or handling that LuaString.valueOf does remain unchanged. + if (value == null) { + return LuaString.valueOf(value); + } + + int h = value.hashCode(); + int idx = (h ^ (h >>> 16)) & STRING_CACHE_MASK; + String key = stringCacheKeys[idx]; + + // Fast positive-hit path: return cached LuaString when key matches. + if (key != null && key.equals(value)) { + return stringCacheValues[idx]; + } + + // Miss: create LuaString and populate the cache slot. + LuaString ls = LuaString.valueOf(value); + stringCacheKeys[idx] = value; + stringCacheValues[idx] = ls; + return ls; } @Override