Skip to content

⚡️ Speed up method BatchDelete.size by 20%#111

Open
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-BatchDelete.size-mmcnkms2
Open

⚡️ Speed up method BatchDelete.size by 20%#111
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-BatchDelete.size-mmcnkms2

Conversation

@codeflash-ai
Copy link
Copy Markdown

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

📄 20% (0.20x) speedup for BatchDelete.size in client/src/com/aerospike/client/BatchDelete.java

⏱️ Runtime : 201 microseconds 167 microseconds (best of 169 runs)

📝 Explanation and details

The change yields a 20% runtime improvement (201µs → 167µs) by caching frequently accessed fields into locals and adding an early-return path when policy is null, which prevents unnecessary work. The key insight is that collapsing repeated object field dereferences (policy, parentPolicy.sendKey) and deferring the call to key.userKey.estimateSize() until the final sendKey decision removes extra virtual reads and method-call overhead on the hot path. The trade-off is a small increase in local variables and a slightly different control-flow shape (an early return) in exchange for lower per-call overhead and no runtime allocations.

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;

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

import com.aerospike.client.BatchDelete;
import com.aerospike.client.Key;
import com.aerospike.client.Value;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.BatchDeletePolicy;
import com.aerospike.client.command.Command;
// Performance comparison:
// BatchDeleteTest.testNoPolicy_parentSendKeyTrue_includesUserKeySize#2: 0.001ms -> 0.001ms (-4.4% faster)
// BatchDeleteTest.testLargeUserKey_sizeScalesLinearly#7: 0.070ms -> 0.077ms (-9.1% faster)
// BatchDeleteTest.testPolicyWithSendKey_true_includesUserKeySize_evenWhenParentFalse#3: 0.001ms -> 0.001ms (13.3% faster)
// BatchDeleteTest.testPolicyWithSendKey_false_parentFalse_excludesUserKeySize#5: 0.000ms -> 0.000ms (-5.4% faster)
// BatchDeleteTest.testEmptyUserKey_whenParentSendKeyTrue_handlesZeroLength#6: 0.000ms -> 0.000ms (-1.3% faster)
// BatchDeleteTest.testNoPolicy_parentSendKeyFalse_sizeIs2#1: 0.000ms -> 0.000ms (-0.7% faster)
// BatchDeleteTest.testPolicyWithSendKey_false_parentTrue_includesUserKeySize#4: 0.000ms -> 0.000ms (-11.0% faster)
// BatchDeleteTest.testNullParentPolicy_throwsNullPointerException#8: 0.051ms -> 0.044ms (15.0% faster)
// BatchDeleteTest.testSize_LargeUserKey_CalculatesCorrectSizeForLargeInput#4: 0.048ms -> 0.011ms (76.6% faster)
// BatchDeleteTest.testSize_NullKey_ThrowsNullPointerException#5: 0.000ms -> 0.000ms (-35.2% faster)
// BatchDeleteTest.testSize_NoSendKeyNeitherPolicy_parentPolicyFalse_ReturnsBaseSize2#3: 0.000ms -> 0.000ms (16.4% faster)
// BatchDeleteTest.testSize_PolicyNonNull_SendKeyTrue_IncludesUserKeySize#2: 0.000ms -> 0.000ms (-15.9% faster)
// BatchDeleteTest.testSize_NullParentPolicyAndNoPolicy_ThrowsNullPointerException#6: 0.028ms -> 0.032ms (-12.5% faster)
// BatchDeleteTest.testSize_PolicyNull_ParentPolicySendKeyTrue_IncludesUserKeySize#1: 0.000ms -> 0.000ms (-22.4% faster)

/**
 * Unit tests for com.aerospike.client.BatchDelete.size(...)
 *
 * NOTE: These tests assume the typical Aerospike client classes (Key, Value, Policy,
 * BatchDeletePolicy, Command) are available on the classpath.
 */
public class BatchDeleteTest {

    private static final String NAMESPACE = "test";
    private static final String SET = "set";

    @Before
    public void setUp() {
        // No shared state required for these tests.
    }

    @Test
    public void testSize_PolicyNull_ParentPolicySendKeyTrue_IncludesUserKeySize() {
        Value userValue = Value.get("abc");
        Key key = new Key(NAMESPACE, SET, userValue);
        BatchDelete bd = new BatchDelete(key);

        Policy parentPolicy = new Policy();
        parentPolicy.sendKey = true;

        int expected = 2 + userValue.estimateSize() + Command.FIELD_HEADER_SIZE + 1;
        int actual = bd.size(parentPolicy, null);

        assertEquals("Size should include user key when parentPolicy.sendKey is true", expected, actual);
    }

    @Test
    public void testSize_PolicyNonNull_SendKeyTrue_IncludesUserKeySize() {
        Value userValue = Value.get("hello");
        Key key = new Key(NAMESPACE, SET, userValue);
        BatchDeletePolicy policy = new BatchDeletePolicy();
        policy.sendKey = true;
        BatchDelete bd = new BatchDelete(policy, key);

        Policy parentPolicy = new Policy(); // default sendKey is false

        int expected = 2 + userValue.estimateSize() + Command.FIELD_HEADER_SIZE + 1;
        int actual = bd.size(parentPolicy, null);

        assertEquals("Size should include user key when policy.sendKey is true", expected, actual);
    }

    @Test
    public void testSize_NoSendKeyNeitherPolicy_parentPolicyFalse_ReturnsBaseSize2() {
        Value userValue = Value.get("value");
        Key key = new Key(NAMESPACE, SET, userValue);
        BatchDelete bd = new BatchDelete(key);

        Policy parentPolicy = new Policy(); // sendKey false by default

        int expected = 2;
        int actual = bd.size(parentPolicy, null);

        assertEquals("Size should be base size (2) when neither policy nor parent requests key", expected, actual);
    }

    @Test
    public void testSize_LargeUserKey_CalculatesCorrectSizeForLargeInput() {
        // Create a large user key (10_000 characters)
        int largeLen = 10_000;
        StringBuilder sb = new StringBuilder(largeLen);
        for (int i = 0; i < largeLen; i++) {
            sb.append('x');
        }
        Value largeValue = Value.get(sb.toString());
        Key key = new Key(NAMESPACE, SET, largeValue);
        BatchDeletePolicy policy = new BatchDeletePolicy();
        policy.sendKey = true;
        BatchDelete bd = new BatchDelete(policy, key);

        Policy parentPolicy = new Policy(); // sendKey false

        int expected = 2 + largeValue.estimateSize() + Command.FIELD_HEADER_SIZE + 1;
        int actual = bd.size(parentPolicy, null);

        assertEquals("Size calculation should work for large user keys", expected, actual);
    }

    @Test(expected = NullPointerException.class)
    public void testSize_NullKey_ThrowsNullPointerException() {
        // Construct with null key; calling size should cause a NullPointerException
        BatchDelete bd = new BatchDelete((Key) null);
        Policy parentPolicy = new Policy();
        bd.size(parentPolicy, null); // Expect NPE due to null key.userKey access
    }

    @Test(expected = NullPointerException.class)
    public void testSize_NullParentPolicyAndNoPolicy_ThrowsNullPointerException() {
        // When policy is null (BatchDelete constructed with Key) and parentPolicy is null,
        // the code attempts to access parentPolicy.sendKey -> should throw NPE.
        Value userValue = Value.get("abc");
        Key key = new Key(NAMESPACE, SET, userValue);
        BatchDelete bd = new BatchDelete(key);

        bd.size(null, null); // Expect NPE because parentPolicy is null
    }
}

To edit these changes git checkout codeflash/optimize-BatchDelete.size-mmcnkms2 and push.

Codeflash Static Badge

The change yields a 20% runtime improvement (201µs → 167µs) by caching frequently accessed fields into locals and adding an early-return path when policy is null, which prevents unnecessary work. The key insight is that collapsing repeated object field dereferences (policy, parentPolicy.sendKey) and deferring the call to key.userKey.estimateSize() until the final sendKey decision removes extra virtual reads and method-call overhead on the hot path. The trade-off is a small increase in local variables and a slightly different control-flow shape (an early return) in exchange for lower per-call overhead and no runtime allocations.
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 March 4, 2026 23:13
@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