Skip to content

Commit ac81ac2

Browse files
committed
Optimize PooledByteBufferAllocator by using bit-based bucket index calculation while preserving existing behaviour.
1 parent a7335a4 commit ac81ac2

1 file changed

Lines changed: 29 additions & 28 deletions

File tree

httpcore5/src/main/java/org/apache/hc/core5/io/PooledByteBufferAllocator.java

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
* greater than the requested capacity; however, their {@link ByteBuffer#limit(int)}
5151
* is set to the requested capacity.
5252
*
53-
* @since 5.7
53+
* @since 5.4
5454
*/
5555
@Contract(threading = ThreadingBehavior.SAFE)
5656
public final class PooledByteBufferAllocator implements ByteBufferAllocator {
@@ -73,8 +73,10 @@ private static final class LocalCache {
7373
final Deque<ByteBuffer>[] directBuckets;
7474

7575
LocalCache(final int bucketCount) {
76-
@SuppressWarnings("unchecked") final Deque<ByteBuffer>[] heap = new Deque[bucketCount];
77-
@SuppressWarnings("unchecked") final Deque<ByteBuffer>[] direct = new Deque[bucketCount];
76+
@SuppressWarnings("unchecked")
77+
final Deque<ByteBuffer>[] heap = new Deque[bucketCount];
78+
@SuppressWarnings("unchecked")
79+
final Deque<ByteBuffer>[] direct = new Deque[bucketCount];
7880
for (int i = 0; i < bucketCount; i++) {
7981
heap[i] = new ArrayDeque<>();
8082
direct[i] = new ArrayDeque<>();
@@ -87,6 +89,7 @@ private static final class LocalCache {
8789

8890
private final int minCapacity;
8991
private final int maxCapacity;
92+
private final int minShift;
9093
private final int[] bucketCapacities;
9194
private final GlobalBucket[] heapBuckets;
9295
private final GlobalBucket[] directBuckets;
@@ -121,9 +124,11 @@ public PooledByteBufferAllocator(
121124
this.maxGlobalPerBucket = maxGlobalPerBucket;
122125
this.maxLocalPerBucket = maxLocalPerBucket;
123126

124-
final int bucketCount = bucketCount(this.minCapacity, this.maxCapacity);
125-
this.bucketCapacities = new int[bucketCount];
127+
this.minShift = Integer.numberOfTrailingZeros(this.minCapacity);
128+
final int maxShift = Integer.numberOfTrailingZeros(this.maxCapacity);
129+
final int bucketCount = maxShift - this.minShift + 1;
126130

131+
this.bucketCapacities = new int[bucketCount];
127132
this.heapBuckets = new GlobalBucket[bucketCount];
128133
this.directBuckets = new GlobalBucket[bucketCount];
129134

@@ -149,30 +154,24 @@ private static int normalizeCapacity(final int capacity) {
149154
return normalized;
150155
}
151156

152-
private static int bucketCount(final int minCapacity, final int maxCapacity) {
153-
int count = 0;
154-
int capacity = minCapacity;
155-
while (capacity <= maxCapacity) {
156-
count++;
157-
capacity <<= 1;
158-
}
159-
return count;
160-
}
161-
157+
/**
158+
* Returns the bucket index for the given capacity, or {@code -1} if the
159+
* capacity is greater than {@code maxCapacity}.
160+
* <p>
161+
* This implementation runs in O(1) using bit operations. It assumes
162+
* {@code minCapacity} and {@code maxCapacity} are powers of two.
163+
*/
162164
private int bucketIndex(final int capacity) {
163165
if (capacity <= minCapacity) {
164166
return 0;
165167
}
166168
if (capacity > maxCapacity) {
167169
return -1;
168170
}
169-
int idx = 0;
170-
int current = minCapacity;
171-
while (current < capacity && current < maxCapacity) {
172-
current <<= 1;
173-
idx++;
174-
}
175-
if (idx >= bucketCapacities.length) {
171+
// Ceil(log2(capacity)) using bit length of (capacity - 1).
172+
final int neededShift = 32 - Integer.numberOfLeadingZeros(capacity - 1);
173+
final int idx = neededShift - minShift;
174+
if (idx < 0 || idx >= bucketCapacities.length) {
176175
return -1;
177176
}
178177
return idx;
@@ -188,8 +187,6 @@ private ByteBuffer allocateInternal(final int requestedCapacity, final boolean d
188187
: ByteBuffer.allocate(requestedCapacity);
189188
}
190189

191-
final int bucketCapacity = bucketCapacities[idx];
192-
193190
final LocalCache cache = localCache.get();
194191
final Deque<ByteBuffer>[] localBuckets = direct
195192
? cache.directBuckets
@@ -214,6 +211,7 @@ private ByteBuffer allocateInternal(final int requestedCapacity, final boolean d
214211
return buf;
215212
}
216213

214+
final int bucketCapacity = bucketCapacities[idx];
217215
buf = direct
218216
? ByteBuffer.allocateDirect(bucketCapacity)
219217
: ByteBuffer.allocate(bucketCapacity);
@@ -249,21 +247,24 @@ public void release(final ByteBuffer buffer) {
249247
: cache.heapBuckets;
250248
final Deque<ByteBuffer> local = localBuckets[idx];
251249

250+
buffer.clear();
251+
buffer.limit(bucketCapacities[idx]);
252+
252253
if (local.size() < maxLocalPerBucket) {
253-
buffer.clear();
254-
buffer.limit(bucketCapacities[idx]);
255254
local.addFirst(buffer);
256255
return;
257256
}
258257

258+
if (maxGlobalPerBucket == 0) {
259+
return;
260+
}
261+
259262
final GlobalBucket[] globalArray = direct ? directBuckets : heapBuckets;
260263
final GlobalBucket global = globalArray[idx];
261264
final int current = global.size.get();
262265
if (current >= maxGlobalPerBucket) {
263266
return;
264267
}
265-
buffer.clear();
266-
buffer.limit(bucketCapacities[idx]);
267268
global.queue.offer(buffer);
268269
global.size.incrementAndGet();
269270
}

0 commit comments

Comments
 (0)