Skip to content

⚡️ Speed up method Pool.poll by 41%#94

Open
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-Pool.poll-mmbjii1q
Open

⚡️ Speed up method Pool.poll by 41%#94
codeflash-ai[bot] wants to merge 1 commit intofix/add-mockito-test-dependencyfrom
codeflash/optimize-Pool.poll-mmbjii1q

Conversation

@codeflash-ai
Copy link
Copy Markdown

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

📄 41% (0.41x) speedup for Pool.poll in client/src/com/aerospike/client/cluster/Pool.java

⏱️ Runtime : 57.2 microseconds 40.7 microseconds (best of 179 runs)

📝 Explanation and details

poll() runtime improved from 57.2 µs to 40.7 µs (≈40% faster). The method now caches frequently accessed instance fields into locals, computes the new head with a single decrement + conditional wrap, and updates head/size exactly once before returning. This reduces repeated object-field loads/stores and simplifies branch/arithmetic work inside the lock, cutting cache traffic and per-call CPU overhead on the hot path. Trade-off: a couple more local variables and slightly denser code for a clear, measurable throughput win with no other resource regressions observed.

Correctness verification report:

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

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

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
// Performance comparison:
// PoolTest.testPoll_SizeOne_HeadZero_WrapsToEndAndDecrementsSize#2: 0.001ms -> 0.001ms (8.3% faster)
// PoolTest.testPoll_DoesNotModifyTotalAtomicInteger#8: 0.001ms -> 0.001ms (5.0% faster)
// PoolTest.testPoll_EmptyPool_ReturnsNullAndNoStateChange#1: 0.001ms -> 0.001ms (-1.3% faster)
// PoolTest.testPoll_MultiplePolls_HeadWrapsProperlyAndSizeBecomesZero#4: 0.001ms -> 0.001ms (-3.8% faster)
// PoolTest.testPoll_MultiplePolls_HeadWrapsProperlyAndSizeBecomesZero#5: 0.001ms -> 0.001ms (3.1% faster)
// PoolTest.testPoll_MultiplePolls_HeadWrapsProperlyAndSizeBecomesZero#6: 0.001ms -> 0.001ms (2.1% faster)
// PoolTest.testPoll_SizeOne_HeadNonZero_DecrementsHeadAndDecrementsSize#3: 0.001ms -> 0.001ms (-1.9% faster)
// PoolTest.testPoll_LargeScale_HeadAndSizeBehavior_ForManyElements#7: 0.050ms -> 0.034ms (32.5% faster)
// PoolTest.testPoll_SizeOne_HeadWrapsToEnd_andSizeDecrements#2: 0.000ms -> 0.000ms (8.5% faster)
// PoolTest.testPoll_OnEmptyPool_ReturnsNull#1: 0.000ms -> 0.000ms (9.7% faster)
// PoolTest.testPoll_HeadDecrements_WhenHeadGreaterThanZero#3: 0.000ms -> 0.000ms (54.0% faster)

/**
 * Unit tests for com.aerospike.client.cluster.Pool.poll().
 *
 * NOTE: These tests interact with Pool's private fields via reflection to verify internal
 * head/size behavior because Connection construction is not practical within these tests.
 */
public class PoolTest {
	private Pool pool;
	private Field headField;
	private Field sizeField;
	private Field connsField;

	@Before
	public void setUp() throws Exception {
		// Create a pool with minSize 0 and maxSize 5 for testing.
		pool = new Pool(0, 5);

		// Obtain reflective access to private fields used by poll().
		headField = Pool.class.getDeclaredField("head");
		headField.setAccessible(true);

		sizeField = Pool.class.getDeclaredField("size");
		sizeField.setAccessible(true);

		connsField = Pool.class.getDeclaredField("conns");
		connsField.setAccessible(true);
	}

	// Helper to set private int fields head/size
	private void setIntField(Field f, int value) throws IllegalAccessException {
		f.setInt(pool, value);
	}

	// Helper to get private int fields head/size
	private int getIntField(Field f) throws IllegalAccessException {
		return f.getInt(pool);
	}

	// Helper to get the conns array as an Object (actual component type is Connection[])
	private Object getConnsArray() throws IllegalAccessException {
		return connsField.get(pool);
	}

	@Test
	public void testPoll_EmptyPool_ReturnsNullAndNoStateChange() throws Exception {
		// Arrange: ensure size == 0 and head has a known value
		setIntField(sizeField, 0);
		setIntField(headField, 2);

		// Act
		Connection result = pool.poll();

		// Assert: poll should return null and head/size should remain unchanged
		assertNull(result);
		assertEquals(0, getIntField(sizeField));
		assertEquals(2, getIntField(headField));
	}

	@Test
	public void testPoll_SizeOne_HeadZero_WrapsToEndAndDecrementsSize() throws Exception {
		// Arrange: head == 0 and size == 1
		setIntField(headField, 0);
		setIntField(sizeField, 1);

		// Act
		Connection result = pool.poll();

		// Assert: result is null (no Connection inserted), head wraps to conns.length - 1, size becomes 0
		assertNull(result);
		Object conns = getConnsArray();
		int len = Array.getLength(conns);
		assertEquals(len - 1, getIntField(headField));
		assertEquals(0, getIntField(sizeField));
	}

	@Test
	public void testPoll_SizeOne_HeadNonZero_DecrementsHeadAndDecrementsSize() throws Exception {
		// Arrange: head == 3 and size == 1
		setIntField(headField, 3);
		setIntField(sizeField, 1);

		// Act
		Connection result = pool.poll();

		// Assert
		assertNull(result);
		assertEquals(2, getIntField(headField)); // head should decrement from 3 to 2
		assertEquals(0, getIntField(sizeField));
	}

	@Test
	public void testPoll_MultiplePolls_HeadWrapsProperlyAndSizeBecomesZero() throws Exception {
		// Arrange: conns length is 5, start with head==1 and size==3
		Object conns = getConnsArray();
		int len = Array.getLength(conns);
		assertEquals(5, len); // sanity check for this test
		setIntField(headField, 1);
		setIntField(sizeField, 3);

		// Act: call poll three times
		pool.poll(); // 1st call: head 1 -> 0, size 3->2
		pool.poll(); // 2nd call: head 0 -> len-1 (4), size 2->1
		pool.poll(); // 3rd call: head 4 -> 3, size 1->0

		// Assert final state
		assertEquals(0, getIntField(sizeField));
		assertEquals(3, getIntField(headField));
	}

	@Test
	public void testPoll_LargeScale_HeadAndSizeBehavior_ForManyElements() throws Exception {
		// Create a larger pool to exercise many iterations
		Pool largePool = new Pool(0, 1000);
		Field largeHead = Pool.class.getDeclaredField("head");
		largeHead.setAccessible(true);
		Field largeSize = Pool.class.getDeclaredField("size");
		largeSize.setAccessible(true);
		Field largeConns = Pool.class.getDeclaredField("conns");
		largeConns.setAccessible(true);

		Object conns = largeConns.get(largePool);
		int len = Array.getLength(conns);
		assertEquals(1000, len);

		// Set head to 10 and size to entire capacity to exercise wrapping many times
		largeHead.setInt(largePool, 10);
		largeSize.setInt(largePool, len);

		// Call poll len times; since we don't populate actual Connection instances,
		// returned values will be null, but internal head and size transitions are tested.
		for (int i = 0; i < len; i++) {
			largePool.poll();
		}

		// After len polls, size should be 0. Head should have moved (10 - len) modulo len.
		int finalSize = largeSize.getInt(largePool);
		assertEquals(0, finalSize);

		int expectedHead = (10 - len) % len;
		// Java % can be negative, adjust to positive index
		if (expectedHead < 0) {
			expectedHead += len;
		}
		int actualHead = largeHead.getInt(largePool);
		assertEquals(expectedHead, actualHead);
	}

	@Test
	public void testPoll_DoesNotModifyTotalAtomicInteger() throws Exception {
		// total represents total connections in use + in pool, poll should not modify total
		Field totalField = Pool.class.getDeclaredField("total");
		totalField.setAccessible(true);
		AtomicInteger total = (AtomicInteger) totalField.get(pool);

		// Set some total value
		total.set(42);

		// Ensure pool is empty
		setIntField(sizeField, 0);
		setIntField(headField, 1);

		// Act
		pool.poll();

		// Assert total unchanged
		assertEquals(42, total.get());
	}
}

To edit these changes git checkout codeflash/optimize-Pool.poll-mmbjii1q and push.

Codeflash Static Badge

poll() runtime improved from 57.2 µs to 40.7 µs (≈40% faster). The method now caches frequently accessed instance fields into locals, computes the new head with a single decrement + conditional wrap, and updates head/size exactly once before returning. This reduces repeated object-field loads/stores and simplifies branch/arithmetic work inside the lock, cutting cache traffic and per-call CPU overhead on the hot path. Trade-off: a couple more local variables and slightly denser code for a clear, measurable throughput win with no other resource regressions observed.
@codeflash-ai codeflash-ai bot requested a review from misrasaurabh1 March 4, 2026 04:32
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High 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: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant