Skip to content

Commit

Permalink
Reduce PoolChunk's metadata size: change LongPriorityQueue to IntPrio…
Browse files Browse the repository at this point in the history
…rityQueue (#13504)

Motivation:

`LongPriorityQueue[]` is used by `PoolChunk` to store available run
information of all page classes.
This information (i.e., `handle`) contains page offset, page count,
isUsed, isSubpage, and bitmapIdx of subpage.

* Handle is inserted to `LongPriorityQueue` and `LongLongHashMap` when
some memory is freed back to the chunk.
* Handle is removed from `LongPriorityQueue` and `LongLongHashMap` when
some memory is allocated.
* One Handle can be split to two handles, and two continuous handles can
be collapsed to one handle.

All the `LongPriorityQueue` operations are only related to page offset
and page count. The low 32bit `bitmapIdx` is not used at all. So the
high 32bit of handle is enough.

Modification:

1) Change `LongPriorityQueue` to `IntPriorityQueue`
2) Store high 32bit handle in `IntPriorityQueue`

Result:

PoolChunk's metadata is smaller.

---------

Co-authored-by: xuesenliang <xuesenliang@tencent.com>
  • Loading branch information
liangxs and xuesenliang committed Jul 25, 2023
1 parent 1b5a3e9 commit 78cbf3a
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
* Internal primitive priority queue, used by {@link PoolChunk}.
* The implementation is based on the binary heap, as described in Algorithms by Sedgewick and Wayne.
*/
final class LongPriorityQueue {
final class IntPriorityQueue {
public static final int NO_VALUE = -1;
private long[] array = new long[9];
private int[] array = new int[9];
private int size;

public void offer(long handle) {
public void offer(int handle) {
if (handle == NO_VALUE) {
throw new IllegalArgumentException("The NO_VALUE (" + NO_VALUE + ") cannot be added to the queue.");
}
Expand All @@ -39,7 +39,7 @@ public void offer(long handle) {
lift(size);
}

public void remove(long value) {
public void remove(int value) {
for (int i = 1; i <= size; i++) {
if (array[i] == value) {
array[i] = array[size--];
Expand All @@ -50,18 +50,18 @@ public void remove(long value) {
}
}

public long peek() {
public int peek() {
if (size == 0) {
return NO_VALUE;
}
return array[1];
}

public long poll() {
public int poll() {
if (size == 0) {
return NO_VALUE;
}
long val = array[1];
int val = array[1];
array[1] = array[size];
array[size] = 0;
size--;
Expand Down Expand Up @@ -100,7 +100,7 @@ private boolean subord(int a, int b) {
}

private void swap(int a, int b) {
long value = array[a];
int value = array[a];
array[a] = array[b];
array[b] = value;
}
Expand Down
28 changes: 14 additions & 14 deletions buffer/src/main/java/io/netty/buffer/PoolChunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ final class PoolChunk<T> implements PoolChunkMetric {
/**
* manage all avail runs
*/
private final LongPriorityQueue[] runsAvail;
private final IntPriorityQueue[] runsAvail;

private final ReentrantLock runsAvailLock;

Expand Down Expand Up @@ -231,18 +231,19 @@ final class PoolChunk<T> implements PoolChunkMetric {
cachedNioBuffers = null;
}

private static LongPriorityQueue[] newRunsAvailqueueArray(int size) {
LongPriorityQueue[] queueArray = new LongPriorityQueue[size];
private static IntPriorityQueue[] newRunsAvailqueueArray(int size) {
IntPriorityQueue[] queueArray = new IntPriorityQueue[size];
for (int i = 0; i < queueArray.length; i++) {
queueArray[i] = new LongPriorityQueue();
queueArray[i] = new IntPriorityQueue();
}
return queueArray;
}

private void insertAvailRun(int runOffset, int pages, long handle) {
int pageIdxFloor = arena.pages2pageIdxFloor(pages);
LongPriorityQueue queue = runsAvail[pageIdxFloor];
queue.offer(handle);
IntPriorityQueue queue = runsAvail[pageIdxFloor];
assert isRun(handle);
queue.offer((int) (handle >> BITMAP_IDX_BIT_LENGTH));

//insert first page of run
insertAvailRun0(runOffset, handle);
Expand All @@ -259,7 +260,7 @@ private void insertAvailRun0(int runOffset, long handle) {

private void removeAvailRun(long handle) {
int pageIdxFloor = arena.pages2pageIdxFloor(runPages(handle));
runsAvail[pageIdxFloor].remove(handle);
runsAvail[pageIdxFloor].remove((int) (handle >> BITMAP_IDX_BIT_LENGTH));
removeAvailRun0(handle);
}

Expand Down Expand Up @@ -348,16 +349,15 @@ private long allocateRun(int runSize) {
}

//get run with min offset in this queue
LongPriorityQueue queue = runsAvail[queueIdx];
IntPriorityQueue queue = runsAvail[queueIdx];
long handle = queue.poll();

assert handle != LongPriorityQueue.NO_VALUE && !isUsed(handle) : "invalid handle: " + handle;
assert handle != IntPriorityQueue.NO_VALUE;
handle <<= BITMAP_IDX_BIT_LENGTH;
assert !isUsed(handle) : "invalid handle: " + handle;

removeAvailRun0(handle);

if (handle != -1) {
handle = splitLargeRun(handle, pages);
}
handle = splitLargeRun(handle, pages);

int pinnedSize = runSize(pageShifts, handle);
freeBytes -= pinnedSize;
Expand Down Expand Up @@ -397,7 +397,7 @@ private int runFirstBestFit(int pageIdx) {
return arena.nPSizes - 1;
}
for (int i = pageIdx; i < arena.nPSizes; i++) {
LongPriorityQueue queue = runsAvail[i];
IntPriorityQueue queue = runsAvail[i];
if (queue != null && !queue.isEmpty()) {
return i;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class LongPriorityQueueTest {
class IntPriorityQueueTest {
@Test
public void mustThrowWhenAddingNoValue() {
final LongPriorityQueue pq = new LongPriorityQueue();
final IntPriorityQueue pq = new IntPriorityQueue();
assertThrows(IllegalArgumentException.class, new Executable() {
@Override
public void execute() {
pq.offer(LongPriorityQueue.NO_VALUE);
pq.offer(IntPriorityQueue.NO_VALUE);
}
});
}
Expand All @@ -42,26 +42,26 @@ public void execute() {
public void mustReturnValuesInOrder() {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int initialValues = tlr.nextInt(5, 30);
ArrayList<Long> values = new ArrayList<Long>();
ArrayList<Integer> values = new ArrayList<Integer>();
for (int i = 0; i < initialValues; i++) {
values.add(tlr.nextLong(0, Long.MAX_VALUE));
values.add(tlr.nextInt(0, Integer.MAX_VALUE));
}
LongPriorityQueue pq = new LongPriorityQueue();
IntPriorityQueue pq = new IntPriorityQueue();
assertTrue(pq.isEmpty());
for (Long value : values) {
for (Integer value : values) {
pq.offer(value);
}
Collections.sort(values);
int valuesToRemove = initialValues / 2;
ListIterator<Long> itr = values.listIterator();
ListIterator<Integer> itr = values.listIterator();
for (int i = 0; i < valuesToRemove; i++) {
assertTrue(itr.hasNext());
assertThat(pq.poll()).isEqualTo(itr.next());
itr.remove();
}
int moreValues = tlr.nextInt(5, 30);
for (int i = 0; i < moreValues; i++) {
long value = tlr.nextLong(0, Long.MAX_VALUE);
int value = tlr.nextInt(0, Integer.MAX_VALUE);
pq.offer(value);
values.add(value);
}
Expand All @@ -71,54 +71,54 @@ public void mustReturnValuesInOrder() {
assertThat(pq.poll()).isEqualTo(itr.next());
}
assertTrue(pq.isEmpty());
assertThat(pq.poll()).isEqualTo(LongPriorityQueue.NO_VALUE);
assertThat(pq.poll()).isEqualTo(IntPriorityQueue.NO_VALUE);
}

@Test
public void internalRemoveOfAllElements() {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int initialValues = tlr.nextInt(5, 30);
ArrayList<Long> values = new ArrayList<Long>();
LongPriorityQueue pq = new LongPriorityQueue();
ArrayList<Integer> values = new ArrayList<Integer>();
IntPriorityQueue pq = new IntPriorityQueue();
for (int i = 0; i < initialValues; i++) {
long value = tlr.nextLong(0, Long.MAX_VALUE);
int value = tlr.nextInt(0, Integer.MAX_VALUE);
pq.offer(value);
values.add(value);
}
for (Long value : values) {
for (Integer value : values) {
pq.remove(value);
}
assertTrue(pq.isEmpty());
assertThat(pq.poll()).isEqualTo(LongPriorityQueue.NO_VALUE);
assertThat(pq.poll()).isEqualTo(IntPriorityQueue.NO_VALUE);
}

@Test
public void internalRemoveMustPreserveOrder() {
ThreadLocalRandom tlr = ThreadLocalRandom.current();
int initialValues = tlr.nextInt(1, 30);
ArrayList<Long> values = new ArrayList<Long>();
LongPriorityQueue pq = new LongPriorityQueue();
ArrayList<Integer> values = new ArrayList<Integer>();
IntPriorityQueue pq = new IntPriorityQueue();
for (int i = 0; i < initialValues; i++) {
long value = tlr.nextLong(0, Long.MAX_VALUE);
int value = tlr.nextInt(0, Integer.MAX_VALUE);
pq.offer(value);
values.add(value);
}

long toRemove = values.get(values.size() / 2);
Integer toRemove = values.get(values.size() / 2);
values.remove(toRemove);
pq.remove(toRemove);

Collections.sort(values);
for (Long value : values) {
for (Integer value : values) {
assertThat(pq.poll()).isEqualTo(value);
}
assertTrue(pq.isEmpty());
assertThat(pq.poll()).isEqualTo(LongPriorityQueue.NO_VALUE);
assertThat(pq.poll()).isEqualTo(IntPriorityQueue.NO_VALUE);
}

@Test
public void mustSupportDuplicateValues() {
LongPriorityQueue pq = new LongPriorityQueue();
IntPriorityQueue pq = new IntPriorityQueue();
pq.offer(10);
pq.offer(5);
pq.offer(6);
Expand All @@ -141,7 +141,7 @@ public void mustSupportDuplicateValues() {
assertThat(pq.poll()).isEqualTo(10);
assertThat(pq.poll()).isEqualTo(10);
assertTrue(pq.isEmpty());
assertThat(pq.poll()).isEqualTo(LongPriorityQueue.NO_VALUE);
assertThat(pq.peek()).isEqualTo(LongPriorityQueue.NO_VALUE);
assertThat(pq.poll()).isEqualTo(IntPriorityQueue.NO_VALUE);
assertThat(pq.peek()).isEqualTo(IntPriorityQueue.NO_VALUE);
}
}

0 comments on commit 78cbf3a

Please sign in to comment.