Skip to content

Commit

Permalink
Make direct buffer allocation optional
Browse files Browse the repository at this point in the history
This is a minor refactoring of all codecs that directly allocate Java `ByteBuffers`: instead of making a direct call to `ByteBuffer#allocate()`, the codec invokes a protected method `makeBuffer(size)`. This allows a user to subclass the codec and override the method to customize the buffer allocation.

In particular, this enables users to use heap-buffers and/or buffer pooling. The latter is essential for reducing memory churn.

In a JVM benchmark (not included) on some real-world data, this refactoring did not decrease performance. (Notice that dropping `final` from some classes does not stop the JVM from inlining, such as in [monomorphic callsites](https://shipilev.net/blog/2015/black-magic-method-dispatch/).)
  • Loading branch information
balthz committed Apr 3, 2017
1 parent 26cb5f8 commit 35fdd72
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*
* @author MURAOKA Taro http://github.com/koron
*/
public final class DeltaZigzagVariableByte implements IntegerCODEC {
public class DeltaZigzagVariableByte implements IntegerCODEC {

@Override
public String toString() {
Expand All @@ -27,7 +27,7 @@ public void compress(int[] inBuf, IntWrapper inPos, int inLen,
return;
}

ByteBuffer byteBuf = ByteBuffer.allocateDirect(inLen * 5 + 3);
ByteBuffer byteBuf = makeBuffer(inLen * 5 + 3);
DeltaZigzagEncoding.Encoder ctx = new DeltaZigzagEncoding.Encoder(0);

// Delta+Zigzag+VariableByte encoding.
Expand Down Expand Up @@ -127,4 +127,15 @@ public void uncompress(int[] inBuf, IntWrapper inPos, int inLen,
outPos.set(op);
inPos.set(inPosLast);
}

/**
* Creates a new buffer of the requested size.
*
* In case you need a different way to allocate buffers, you can override this method
* with a custom behavior. The default implementation allocates a new Java direct
* {@link ByteBuffer} on each invocation.
*/
protected ByteBuffer makeBuffer(int sizeInBytes) {
return ByteBuffer.allocateDirect(sizeInBytes);
}
}
15 changes: 13 additions & 2 deletions src/main/java/me/lemire/integercompression/FastPFOR.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
*
* @author Daniel Lemire
*/
public final class FastPFOR implements IntegerCODEC,SkippableIntegerCODEC {
public class FastPFOR implements IntegerCODEC,SkippableIntegerCODEC {
final static int OVERHEAD_OF_EACH_EXCEPT = 8;
/**
*
Expand Down Expand Up @@ -68,7 +68,7 @@ public final class FastPFOR implements IntegerCODEC,SkippableIntegerCODEC {
private FastPFOR(int pagesize) {
pageSize = pagesize;
// Initiate arrrays.
byteContainer = ByteBuffer.allocateDirect(3 * pageSize
byteContainer = makeBuffer(3 * pageSize
/ BLOCK_SIZE + pageSize);
byteContainer.order(ByteOrder.LITTLE_ENDIAN);
for (int k = 1; k < dataTobePacked.length; ++k)
Expand Down Expand Up @@ -329,4 +329,15 @@ public void uncompress(int[] in, IntWrapper inpos, int inlength, int[] out,
public String toString() {
return this.getClass().getSimpleName();
}

/**
* Creates a new buffer of the requested size.
*
* In case you need a different way to allocate buffers, you can override this method
* with a custom behavior. The default implementation allocates a new Java direct
* {@link ByteBuffer} on each invocation.
*/
protected ByteBuffer makeBuffer(int sizeInBytes) {
return ByteBuffer.allocateDirect(sizeInBytes);
}
}
15 changes: 13 additions & 2 deletions src/main/java/me/lemire/integercompression/FastPFOR128.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
* @author Daniel Lemire
*/
public final class FastPFOR128 implements IntegerCODEC,SkippableIntegerCODEC {
public class FastPFOR128 implements IntegerCODEC,SkippableIntegerCODEC {
final static int OVERHEAD_OF_EACH_EXCEPT = 8;
/**
*
Expand Down Expand Up @@ -50,7 +50,7 @@ public final class FastPFOR128 implements IntegerCODEC,SkippableIntegerCODEC {
public FastPFOR128(int pagesize) {
pageSize = pagesize;
// Initiate arrrays.
byteContainer = ByteBuffer.allocateDirect(3 * pageSize
byteContainer = makeBuffer(3 * pageSize
/ BLOCK_SIZE + pageSize);
byteContainer.order(ByteOrder.LITTLE_ENDIAN);
for (int k = 1; k < dataTobePacked.length; ++k)
Expand Down Expand Up @@ -310,4 +310,15 @@ public void uncompress(int[] in, IntWrapper inpos, int inlength, int[] out,
public String toString() {
return this.getClass().getSimpleName();
}

/**
* Creates a new buffer of the requested size.
*
* In case you need a different way to allocate buffers, you can override this method
* with a custom behavior. The default implementation allocates a new Java direct
* {@link ByteBuffer} on each invocation.
*/
protected ByteBuffer makeBuffer(int sizeInBytes) {
return ByteBuffer.allocateDirect(sizeInBytes);
}
}
12 changes: 11 additions & 1 deletion src/main/java/me/lemire/integercompression/VariableByte.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void headlessCompress(int[] in, IntWrapper inpos, int inlength, int[] out
IntWrapper outpos) {
if (inlength == 0)
return;
ByteBuffer buf = ByteBuffer.allocateDirect(inlength * 8);
ByteBuffer buf = makeBuffer(inlength * 8);
buf.order(ByteOrder.LITTLE_ENDIAN);
for (int k = inpos.get(); k < inpos.get() + inlength; ++k) {
final long val = in[k] & 0xFFFFFFFFL; // To be consistent with
Expand Down Expand Up @@ -202,4 +202,14 @@ public void headlessUncompress(int[] in, IntWrapper inpos, int inlength, int[] o
inpos.set(p + (s!=0 ? 1 : 0));
}

/**
* Creates a new buffer of the requested size.
*
* In case you need a different way to allocate buffers, you can override this method
* with a custom behavior. The default implementation allocates a new Java direct
* {@link ByteBuffer} on each invocation.
*/
protected ByteBuffer makeBuffer(int sizeInBytes) {
return ByteBuffer.allocateDirect(sizeInBytes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void compress(int[] in, IntWrapper inpos, int inlength,
if (inlength == 0)
return;
int initoffset = 0;
ByteBuffer buf = ByteBuffer.allocateDirect(inlength * 8);
ByteBuffer buf = makeBuffer(inlength * 8);
buf.order(ByteOrder.LITTLE_ENDIAN);
for (int k = inpos.get(); k < inpos.get() + inlength; ++k) {
final long val = (in[k] - initoffset) & 0xFFFFFFFFL; // To be consistent with unsigned integers in C/C++
Expand Down Expand Up @@ -187,7 +187,7 @@ public void headlessCompress(int[] in, IntWrapper inpos, int inlength,
return;
int initoffset = initvalue.get();
initvalue.set(in[inpos.get()+inlength -1]);
ByteBuffer buf = ByteBuffer.allocateDirect(inlength * 8);
ByteBuffer buf = makeBuffer(inlength * 8);
buf.order(ByteOrder.LITTLE_ENDIAN);
for (int k = inpos.get(); k < inpos.get() + inlength; ++k) {
final long val = (in[k] - initoffset) & 0xFFFFFFFFL; // To be consistent with unsigned integers in C/C++
Expand Down Expand Up @@ -253,4 +253,14 @@ public void headlessUncompress(int[] in, IntWrapper inpos, int inlength,
inpos.set(p + (s!=0 ? 1 : 0));
}

/**
* Creates a new buffer of the requested size.
*
* In case you need a different way to allocate buffers, you can override this method
* with a custom behavior. The default implementation allocates a new Java direct
* {@link ByteBuffer} on each invocation.
*/
protected ByteBuffer makeBuffer(int sizeInBytes) {
return ByteBuffer.allocateDirect(sizeInBytes);
}
}

0 comments on commit 35fdd72

Please sign in to comment.