mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Support an up-front block allocation in DefaultAllocator.
This commit is contained in:
parent
79d557dd80
commit
91bcec82f5
1 changed files with 78 additions and 15 deletions
|
|
@ -25,41 +25,72 @@ import java.util.Arrays;
|
||||||
*/
|
*/
|
||||||
public final class DefaultAllocator implements Allocator {
|
public final class DefaultAllocator implements Allocator {
|
||||||
|
|
||||||
private static final int INITIAL_RECYCLED_ALLOCATION_CAPACITY = 100;
|
private static final int AVAILABLE_EXTRA_CAPACITY = 100;
|
||||||
|
|
||||||
private final int individualAllocationSize;
|
private final int individualAllocationSize;
|
||||||
|
private final byte[] initialAllocationBlock;
|
||||||
|
|
||||||
private int allocatedCount;
|
private int allocatedCount;
|
||||||
private int recycledCount;
|
private int availableCount;
|
||||||
private Allocation[] recycledAllocations;
|
private Allocation[] availableAllocations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an empty pool.
|
* Constructs an initially empty pool.
|
||||||
*
|
*
|
||||||
* @param individualAllocationSize The length of each individual allocation.
|
* @param individualAllocationSize The length of each individual allocation.
|
||||||
*/
|
*/
|
||||||
public DefaultAllocator(int individualAllocationSize) {
|
public DefaultAllocator(int individualAllocationSize) {
|
||||||
|
this(individualAllocationSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a pool with some {@link Allocation}s created up front.
|
||||||
|
* <p>
|
||||||
|
* Note: Initial {@link Allocation}s will never be discarded by {@link #trim(int)}.
|
||||||
|
*
|
||||||
|
* @param individualAllocationSize The length of each individual allocation.
|
||||||
|
* @param initialAllocationCount The number of allocations to create up front.
|
||||||
|
*/
|
||||||
|
public DefaultAllocator(int individualAllocationSize, int initialAllocationCount) {
|
||||||
Assertions.checkArgument(individualAllocationSize > 0);
|
Assertions.checkArgument(individualAllocationSize > 0);
|
||||||
|
Assertions.checkArgument(initialAllocationCount >= 0);
|
||||||
this.individualAllocationSize = individualAllocationSize;
|
this.individualAllocationSize = individualAllocationSize;
|
||||||
this.recycledAllocations = new Allocation[INITIAL_RECYCLED_ALLOCATION_CAPACITY];
|
this.availableCount = initialAllocationCount;
|
||||||
|
this.availableAllocations = new Allocation[initialAllocationCount + AVAILABLE_EXTRA_CAPACITY];
|
||||||
|
if (initialAllocationCount > 0) {
|
||||||
|
initialAllocationBlock = new byte[initialAllocationCount * individualAllocationSize];
|
||||||
|
for (int i = 0; i < initialAllocationCount; i++) {
|
||||||
|
int allocationOffset = i * individualAllocationSize;
|
||||||
|
availableAllocations[i] = new Allocation(initialAllocationBlock, allocationOffset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initialAllocationBlock = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized Allocation allocate() {
|
public synchronized Allocation allocate() {
|
||||||
allocatedCount++;
|
allocatedCount++;
|
||||||
return recycledCount > 0 ? recycledAllocations[--recycledCount]
|
Allocation allocation;
|
||||||
: new Allocation(new byte[individualAllocationSize], 0);
|
if (availableCount > 0) {
|
||||||
|
allocation = availableAllocations[--availableCount];
|
||||||
|
availableAllocations[availableCount] = null;
|
||||||
|
} else {
|
||||||
|
allocation = new Allocation(new byte[individualAllocationSize], 0);
|
||||||
|
}
|
||||||
|
return allocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void release(Allocation allocation) {
|
public synchronized void release(Allocation allocation) {
|
||||||
// Weak sanity check that the allocation probably originated from this pool.
|
// Weak sanity check that the allocation probably originated from this pool.
|
||||||
Assertions.checkArgument(allocation.data.length == individualAllocationSize);
|
Assertions.checkArgument(allocation.data == initialAllocationBlock
|
||||||
|
|| allocation.data.length == individualAllocationSize);
|
||||||
allocatedCount--;
|
allocatedCount--;
|
||||||
if (recycledCount == recycledAllocations.length) {
|
if (availableCount == availableAllocations.length) {
|
||||||
recycledAllocations = Arrays.copyOf(recycledAllocations, recycledAllocations.length * 2);
|
availableAllocations = Arrays.copyOf(availableAllocations, availableAllocations.length * 2);
|
||||||
}
|
}
|
||||||
recycledAllocations[recycledCount++] = allocation;
|
availableAllocations[availableCount++] = allocation;
|
||||||
// Wake up threads waiting for the allocated size to drop.
|
// Wake up threads waiting for the allocated size to drop.
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
|
|
@ -67,11 +98,43 @@ public final class DefaultAllocator implements Allocator {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void trim(int targetSize) {
|
public synchronized void trim(int targetSize) {
|
||||||
int targetAllocationCount = Util.ceilDivide(targetSize, individualAllocationSize);
|
int targetAllocationCount = Util.ceilDivide(targetSize, individualAllocationSize);
|
||||||
int targetRecycledAllocationCount = Math.max(0, targetAllocationCount - allocatedCount);
|
int targetAvailableCount = Math.max(0, targetAllocationCount - allocatedCount);
|
||||||
if (targetRecycledAllocationCount < recycledCount) {
|
if (targetAvailableCount >= availableCount) {
|
||||||
Arrays.fill(recycledAllocations, targetRecycledAllocationCount, recycledCount, null);
|
// We're already at or below the target.
|
||||||
recycledCount = targetRecycledAllocationCount;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initialAllocationBlock != null) {
|
||||||
|
// Some allocations are backed by an initial block. We need to make sure that we hold onto all
|
||||||
|
// such allocations. Re-order the available allocations so that the ones backed by the initial
|
||||||
|
// block come first.
|
||||||
|
int lowIndex = 0;
|
||||||
|
int highIndex = availableCount - 1;
|
||||||
|
while (lowIndex <= highIndex) {
|
||||||
|
Allocation lowAllocation = availableAllocations[lowIndex];
|
||||||
|
if (lowAllocation.data == initialAllocationBlock) {
|
||||||
|
lowIndex++;
|
||||||
|
} else {
|
||||||
|
Allocation highAllocation = availableAllocations[lowIndex];
|
||||||
|
if (highAllocation.data != initialAllocationBlock) {
|
||||||
|
highIndex--;
|
||||||
|
} else {
|
||||||
|
availableAllocations[lowIndex++] = highAllocation;
|
||||||
|
availableAllocations[highIndex--] = lowAllocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// lowIndex is the index of the first allocation not backed by an initial block.
|
||||||
|
targetAvailableCount = Math.max(targetAvailableCount, lowIndex);
|
||||||
|
if (targetAvailableCount >= availableCount) {
|
||||||
|
// We're already at or below the target.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard allocations beyond the target.
|
||||||
|
Arrays.fill(availableAllocations, targetAvailableCount, availableCount, null);
|
||||||
|
availableCount = targetAvailableCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue