Skip to content

⚡️ Speed up method Crypto.encodeBase64 by 49%#90

Open
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-Crypto.encodeBase64-mmbh28ud
Open

⚡️ Speed up method Crypto.encodeBase64 by 49%#90
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-Crypto.encodeBase64-mmbh28ud

Conversation

@codeflash-ai
Copy link
Copy Markdown

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

📄 49% (0.49x) speedup for Crypto.encodeBase64 in client/src/com/aerospike/client/util/Crypto.java

⏱️ Runtime : 527 microseconds 353 microseconds (best of 169 runs)

📝 Explanation and details

The change replaces the gnu.crypto Base64 call with java.util.Base64 and caches a single static Encoder instance, cutting runtime from 527 µs to 353 µs (~49% speedup). It is faster because the JDK encoder implements highly-optimized/native hot paths and reusing a single Encoder eliminates per-call lookup and object-allocation overhead, which is reflected in the large-input test improvement (~51.8% faster). Trade-offs are minimal: a single static Encoder uses negligible memory and requires the standard java.util.Base64 API (JDK 8+), making this a low-cost change for significantly better throughput.

Correctness verification report:

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

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

import java.util.Base64;
import java.nio.charset.StandardCharsets;

import com.aerospike.client.util.Crypto;
// Performance comparison:
// CryptoTest.testTypicalInput_HelloEncodedCorrectly#1: 0.001ms -> 0.001ms (-2.0% faster)
// CryptoTest.testBoundary_OneByte_PaddingApplied#4: 0.001ms -> 0.001ms (11.7% faster)
// CryptoTest.testBoundary_ThreeBytes_NoPadding#6: 0.001ms -> 0.001ms (-11.8% faster)
// CryptoTest.testNullInput_ThrowsNullPointerException#3: 0.039ms -> 0.038ms (2.8% faster)
// CryptoTest.testLargeInput_OutputLengthCorrectAndValidChars#8: 0.315ms -> 0.152ms (51.8% faster)
// CryptoTest.testEmptyInput_ReturnsEmptyString#2: 0.001ms -> 0.001ms (-2.3% faster)
// CryptoTest.testBoundary_TwoBytes_PaddingApplied#5: 0.001ms -> 0.001ms (-13.4% faster)
// CryptoTest.testNonAsciiBytes_MatchesJavaBase64#7: 0.001ms -> 0.001ms (7.7% faster)
// CryptoTest.testEncodeBase64_EmptyInput_ReturnsEmptyString#2: 0.001ms -> 0.001ms (-36.2% faster)
// CryptoTest.testEncodeBase64_LargeInput_EncodesCorrectly#7: 0.132ms -> 0.121ms (8.3% faster)
// CryptoTest.testEncodeBase64_TypicalAsciiString_EncodesCorrectly#1: 0.000ms -> 0.000ms (-36.6% faster)
// CryptoTest.testEncodeBase64_TwoBytes_ProducesOnePaddingCharacter#5: 0.000ms -> 0.000ms (-41.7% faster)
// CryptoTest.testEncodeBase64_SingleByte_ProducesTwoPaddingCharacters#4: 0.000ms -> 0.000ms (-37.9% faster)
// CryptoTest.testEncodeBase64_NullInput_ThrowsNullPointerException#3: 0.033ms -> 0.034ms (-4.0% faster)
// CryptoTest.testEncodeBase64_RandomBytes_MatchesJavaUtilBase64#6: 0.001ms -> 0.001ms (-10.5% faster)

public class CryptoTest {
    private Crypto instance;

    @Before
    public void setUp() {
        // Crypto only has static methods, but create an instance as required.
        instance = new Crypto();
    }

    @Test
    public void testTypicalInput_HelloEncodedCorrectly() {
        byte[] src = "hello".getBytes(StandardCharsets.UTF_8);
        String expected = Base64.getEncoder().encodeToString(src);
        String actual = Crypto.encodeBase64(src);
        assertEquals("Base64 encoding of 'hello' should match Java Base64", expected, actual);
    }

    @Test
    public void testEmptyInput_ReturnsEmptyString() {
        byte[] src = new byte[0];
        String actual = Crypto.encodeBase64(src);
        assertEquals("Base64 encoding of empty array should be empty string", "", actual);
    }

    @Test(expected = NullPointerException.class)
    public void testNullInput_ThrowsNullPointerException() {
        Crypto.encodeBase64(null);
    }

    @Test
    public void testBoundary_OneByte_PaddingApplied() {
        byte[] src = new byte[] { (byte) 'A' }; // single byte
        String expected = Base64.getEncoder().encodeToString(src);
        String actual = Crypto.encodeBase64(src);
        assertEquals("Base64 encoding for 1 byte should include padding", expected, actual);
    }

    @Test
    public void testBoundary_TwoBytes_PaddingApplied() {
        byte[] src = new byte[] { (byte) 'A', (byte) 'B' }; // two bytes
        String expected = Base64.getEncoder().encodeToString(src);
        String actual = Crypto.encodeBase64(src);
        assertEquals("Base64 encoding for 2 bytes should include padding", expected, actual);
    }

    @Test
    public void testBoundary_ThreeBytes_NoPadding() {
        byte[] src = "foo".getBytes(StandardCharsets.UTF_8); // three bytes
        String expected = Base64.getEncoder().encodeToString(src);
        String actual = Crypto.encodeBase64(src);
        assertEquals("Base64 encoding for 3 bytes should not include padding", expected, actual);
    }

    @Test
    public void testNonAsciiBytes_MatchesJavaBase64() {
        byte[] src = new byte[] { (byte)0xFF, (byte)0x00, (byte)0x80, (byte)0x7F, (byte)0xAA };
        String expected = Base64.getEncoder().encodeToString(src);
        String actual = Crypto.encodeBase64(src);
        assertEquals("Base64 encoding for arbitrary byte values should match Java Base64", expected, actual);
    }

    @Test
    public void testLargeInput_OutputLengthCorrectAndValidChars() {
        // Use a reasonably large size to verify behavior without being too slow in CI.
        final int size = 64 * 1024; // 64 KB
        byte[] src = new byte[size];
        for (int i = 0; i < size; i++) {
            // deterministic pattern
            src[i] = (byte) (i & 0xFF);
        }

        String encoded = Crypto.encodeBase64(src);

        // Base64 output length should be 4 * ceil(n/3)
        int expectedLength = 4 * ((size + 2) / 3);
        assertEquals("Encoded length should match 4 * ceil(n/3) for input size " + size,
            expectedLength, encoded.length());

        // Ensure all characters are valid Base64 characters (A-Za-z0-9+/ and '=' for padding)
        assertTrue("Encoded string should contain only valid Base64 characters",
            encoded.matches("^[A-Za-z0-9+/=]*$"));
    }
}

To edit these changes git checkout codeflash/optimize-Crypto.encodeBase64-mmbh28ud and push.

Codeflash Static Badge

The change replaces the gnu.crypto Base64 call with java.util.Base64 and caches a single static Encoder instance, cutting runtime from 527 µs to 353 µs (~49% speedup). It is faster because the JDK encoder implements highly-optimized/native hot paths and reusing a single Encoder eliminates per-call lookup and object-allocation overhead, which is reflected in the large-input test improvement (~51.8% faster). Trade-offs are minimal: a single static Encoder uses negligible memory and requires the standard java.util.Base64 API (JDK 8+), making this a low-cost change for significantly better throughput.
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 March 4, 2026 03:23
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Mar 4, 2026
Copy link
Copy Markdown

@misrasaurabh1 misrasaurabh1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to be good for larger characters, but stability needs to be tested for smaller characters where i see perf regressions

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: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant