Skip to content

⚡️ Speed up method LuaUnpacker.getString by 5%#105

Open
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-LuaUnpacker.getString-mmc7voet
Open

⚡️ Speed up method LuaUnpacker.getString by 5%#105
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-LuaUnpacker.getString-mmc7voet

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai bot commented Mar 4, 2026

📄 5% (0.05x) speedup for LuaUnpacker.getString in client/src/com/aerospike/client/lua/LuaUnpacker.java

⏱️ Runtime : 426 microseconds 405 microseconds (best of 169 runs)

📝 Explanation and details

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.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 14 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 0.0%
🌀 Click to see Generated Regression Tests
package com.aerospike.client.lua;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

import org.luaj.vm2.LuaString;
// Performance comparison:
// LuaUnpackerTest.testEmptyString_returnsEmptyLuaString#2: 0.001ms -> 0.001ms (1.9% faster)
// LuaUnpackerTest.testNullString_throwsNullPointerException#3: 0.038ms -> 0.036ms (5.1% faster)
// LuaUnpackerTest.testStringWithNullCharacter_preservesContent#5: 0.002ms -> 0.002ms (-1.2% faster)
// LuaUnpackerTest.testLargeString_returnsLuaStringOfSameContentAndLength#6: 0.122ms -> 0.120ms (1.3% faster)
// LuaUnpackerTest.testTypicalString_returnsLuaStringEquivalent#1: 0.002ms -> 0.002ms (11.5% faster)
// LuaUnpackerTest.testUnicodeString_preservesContent#4: 0.003ms -> 0.003ms (19.6% faster)
// LuaUnpackerTest.testNullInput_ThrowsNullPointerException#3: 0.037ms -> 0.034ms (6.8% faster)
// LuaUnpackerTest.testUnicodeAndSpecialCharacters_Preserved#4: 0.004ms -> 0.004ms (-4.8% faster)
// LuaUnpackerTest.testMultipleSequentialCalls_ConsistentResults#5: 0.002ms -> 0.002ms (-7.2% faster)
// LuaUnpackerTest.testMultipleSequentialCalls_ConsistentResults#6: 0.002ms -> 0.002ms (-1.9% faster)
// LuaUnpackerTest.testMultipleSequentialCalls_ConsistentResults#7: 0.002ms -> 0.002ms (-1.0% faster)
// LuaUnpackerTest.testEmptyString_ReturnsEmptyLuaString#2: 0.001ms -> 0.001ms (16.3% faster)
// LuaUnpackerTest.testTypicalString_ReturnsLuaStringWithSameContent#1: 0.002ms -> 0.002ms (10.6% faster)
// LuaUnpackerTest.testLargeString_PerformanceAndCorrectness#8: 0.208ms -> 0.195ms (6.6% faster)

/*
 * Tests for com.aerospike.client.lua.LuaUnpacker.getString
 *
 * The LuaUnpacker.getString method simply delegates to org.luaj.vm2.LuaString.valueOf(String).
 * These tests verify typical behavior, edge cases, error conditions, and large inputs.
 */
public class LuaUnpackerTest {
	private LuaUnpacker unpacker;

	@Before
	public void setUp() {
		// LuaUnpacker constructor requires a LuaInstance and buffer parameters.
		// The getString method does not use these fields, so null and an empty buffer are sufficient.
		unpacker = new LuaUnpacker(null, new byte[0], 0, 0);
	}

	@Test
	public void testTypicalString_ReturnsLuaStringWithSameContent() {
		String input = "hello";
		LuaString result = unpacker.getString(input);
		assertNotNull("Result should not be null for a typical string", result);
		assertEquals("LuaString should contain the original content", input, result.tojstring());
	}

	@Test
	public void testEmptyString_ReturnsEmptyLuaString() {
		String input = "";
		LuaString result = unpacker.getString(input);
		assertNotNull("Result should not be null for empty string", result);
		assertEquals("LuaString should be empty", input, result.tojstring());
	}

	@Test(expected = NullPointerException.class)
	public void testNullInput_ThrowsNullPointerException() {
		// LuaString.valueOf(null) is expected to throw NullPointerException.
		unpacker.getString(null);
	}

	@Test
	public void testUnicodeAndSpecialCharacters_Preserved() {
		String input = "Line1\nLine2\r\n\u0000\u2603\uD83D\uDE0A"; // includes newline, null-char, snowman, emoji
		LuaString result = unpacker.getString(input);
		assertNotNull("Result should not be null for unicode and special characters", result);
		assertEquals("Unicode and special characters should be preserved", input, result.tojstring());
	}

	@Test
	public void testMultipleSequentialCalls_ConsistentResults() {
		String a = "first";
		String b = "second";
		String c = "third";

		LuaString ra = unpacker.getString(a);
		LuaString rb = unpacker.getString(b);
		LuaString rc = unpacker.getString(c);

		assertEquals("first call should match", a, ra.tojstring());
		assertEquals("second call should match", b, rb.tojstring());
		assertEquals("third call should match", c, rc.tojstring());
	}

	@Test
	public void testLargeString_PerformanceAndCorrectness() {
		// Large-scale input: build a large string (200k chars) to validate correctness and reasonable performance.
		int size = 200_000;
		StringBuilder sb = new StringBuilder(size);
		for (int i = 0; i < size; i++) {
			sb.append((char) ('a' + (i % 26)));
		}
		String large = sb.toString();

		long start = System.currentTimeMillis();
		LuaString result = unpacker.getString(large);
		long duration = System.currentTimeMillis() - start;

		assertNotNull("Result should not be null for large string", result);
		assertEquals("Large string content should be preserved", large, result.tojstring());
		assertEquals("LuaString length should match original", large.length(), result.tojstring().length());

		// A very loose performance sanity check to catch pathological behavior in CI.
		// This does not enforce strict timing, just ensures it completes in a reasonable amount of time.
		assertTrue("getString should handle large inputs reasonably quickly (under 10s)", duration < 10_000);
	}
}

To edit these changes git checkout codeflash/optimize-LuaUnpacker.getString-mmc7voet and push.

Codeflash Static Badge

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.
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 March 4, 2026 15:54
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: Medium Optimization Quality according to Codeflash labels Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: Medium Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants