Impl of pooling for arrays and buckets of arrays of sizes

This commit is contained in:
Collin Smith 2021-07-21 15:08:46 -07:00
parent 16670d6e89
commit 5bc47dbf98
3 changed files with 224 additions and 0 deletions

View File

@ -0,0 +1,49 @@
package com.riiablo.map2.util;
import java.lang.reflect.Array;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.badlogic.gdx.utils.Pool;
public class ArrayPool<E> extends Pool<E> {
static <E> E create(Class<E> clazz, int length) {
return clazz.cast(Array.newInstance(clazz.getComponentType(), length));
}
public static <E> ArrayPool<E> get(Class<E> clazz, int length) {
return new ArrayPool<>(clazz, length, 16, 16);
}
public static <E> Pool<E> get(
Class<E> clazz,
int length,
int initialCapacity,
int maxCapacity
) {
return new ArrayPool<>(clazz, length, initialCapacity, maxCapacity);
}
final Class<E> clazz;
final int length;
ArrayPool(Class<E> clazz, int length, int initialCapacity, int maxCapacity) {
super(initialCapacity, maxCapacity);
this.clazz = clazz;
this.length = length;
}
@Override
protected E newObject() {
return create(clazz, length);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("clazz", clazz.getSimpleName())
.append("length", length)
.append("max", max)
.append("peak", peak)
.toString();
}
}

View File

@ -0,0 +1,98 @@
package com.riiablo.map2.util;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.Pool;
import com.riiablo.logger.LogManager;
import com.riiablo.logger.Logger;
public class BucketPool<E> {
private static final Logger log = LogManager.getLogger(BucketPool.class);
final Class<E> clazz;
final Array<ArrayPool<E>> pools;
BucketPool(Class<E> clazz, int[] lengths, int offset, int length) {
this.clazz = clazz;
Arrays.sort(lengths, offset, offset + length);
pools = new Array<>(ArrayPool.class);
for (int i = offset, s = i + length; i < s; i++) {
pools.add(ArrayPool.get(clazz, lengths[i]));
}
}
public Pool<E> get(int length) {
for (ArrayPool<E> pool : pools) {
if (length <= pool.length) {
return pool;
}
}
throw new NoSuchElementException("No bucket big enough for length: " + length);
}
public E obtain(int length) {
try {
Pool<E> pool = get(length);
return pool.obtain();
} catch (NoSuchElementException t) {
E instance = ArrayPool.create(clazz, length);
log.debugf("obtain custom-sized array instance: 0x%h %s length %d",
System.identityHashCode(instance),
clazz.getSimpleName(),
length);
return instance;
}
}
public void free(E o) {
int length = ArrayUtils.getLength(o);
if (length <= 0) return;
try {
Pool<E> pool = get(length);
pool.free(o);
} catch (NoSuchElementException t) {
log.debugf("free custom-sized array instance: 0x%h %s length %d",
System.identityHashCode(o),
clazz.getSimpleName(),
length);
}
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("pools", pools)
.toString();
}
public static <E> Builder<E> builder(Class<E> clazz) {
if (!clazz.isArray()) throw new IllegalArgumentException("clazz must be an array type");
return new Builder<>(clazz);
}
public static class Builder<E> {
final Class<E> clazz;
final IntArray lengths;
Builder(Class<E> clazz) {
this.clazz = clazz;
lengths = new IntArray(4);
}
public Builder<E> add(int length) {
lengths.add(length);
return this;
}
public BucketPool<E> build() {
return new BucketPool<>(clazz, lengths.items, 0, lengths.size);
}
}
}

View File

@ -0,0 +1,77 @@
package com.riiablo.map2.util;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.NoSuchElementException;
import com.riiablo.logger.Level;
import com.riiablo.logger.LogManager;
import com.riiablo.logger.OutputStreamAppender;
class BucketPoolTest {
@BeforeAll
static void before() {
LogManager.getRootLogger().addAppender(new OutputStreamAppender(System.out));
LogManager.setLevel("com.riiablo.map2.util", Level.TRACE);
}
static BucketPool<byte[]> newByteInstance() {
return BucketPool
.builder(byte[].class)
.add(16)
.add(32)
.add(64)
.build();
}
@Test
void byte_array_pool() {
BucketPool<byte[]> buckets = newByteInstance();
assertEquals(3, buckets.pools.size);
assertEquals(16, buckets.pools.get(0).length);
assertEquals(32, buckets.pools.get(1).length);
assertEquals(64, buckets.pools.get(2).length);
}
@Test
void byte_array_pool_get() {
BucketPool<byte[]> buckets = newByteInstance();
assertEquals(buckets.get(15), buckets.pools.get(0));
assertEquals(buckets.get(16), buckets.pools.get(0));
assertEquals(buckets.get(31), buckets.pools.get(1));
assertEquals(buckets.get(32), buckets.pools.get(1));
assertEquals(buckets.get(63), buckets.pools.get(2));
assertEquals(buckets.get(64), buckets.pools.get(2));
}
@Test
void byte_array_pool_get_high() {
BucketPool<byte[]> buckets = newByteInstance();
assertThrows(NoSuchElementException.class, () -> buckets.get(65));
}
@Test
void byte_array_pool_obtain_15() {
BucketPool<byte[]> buckets = newByteInstance();
byte[] data = buckets.obtain(15);
assertTrue(data.length >= 15);
buckets.free(data);
}
@Test
void byte_array_pool_obtain_16() {
BucketPool<byte[]> buckets = newByteInstance();
byte[] data = buckets.obtain(16);
assertTrue(data.length >= 16);
buckets.free(data);
}
@Test
void byte_array_pool_obtain_65() {
BucketPool<byte[]> buckets = newByteInstance();
byte[] data = buckets.obtain(65);
assertTrue(data.length >= 65);
buckets.free(data);
}
}