Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[issues-912] optimize serialization size for primitive arrays - IntOnly for now #915

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/com/esotericsoftware/kryo/Kryo.java
Expand Up @@ -162,6 +162,7 @@ public class Kryo {
private IdentityMap originalToCopy;
private Object needsCopyReference;
private Generics generics = new DefaultGenerics(this);
public boolean optimizePrimitiveArrays = false;

/** Creates a new Kryo with a {@link DefaultClassResolver} and references disabled. */
public Kryo () {
Expand Down Expand Up @@ -1294,6 +1295,14 @@ public void setOptimizedGenerics (boolean optimizedGenerics) {
generics = optimizedGenerics ? new DefaultGenerics(this) : NoGenerics.INSTANCE;
}

public boolean isOptimizePrimitiveArrays() {
return optimizePrimitiveArrays;
}

public void setOptimizePrimitiveArrays(boolean optimizePrimitiveArraySize) {
this.optimizePrimitiveArrays = optimizePrimitiveArraySize;
}

static final class DefaultSerializerEntry {
final Class type;
final SerializerFactory serializerFactory;
Expand Down
37 changes: 21 additions & 16 deletions src/com/esotericsoftware/kryo/io/Input.java
Expand Up @@ -19,6 +19,7 @@

package com.esotericsoftware.kryo.io;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.KryoBufferUnderflowException;
import com.esotericsoftware.kryo.util.Pool.Poolable;
Expand Down Expand Up @@ -902,39 +903,43 @@ private String readAscii_slow (int charCount) {
}

// Primitive arrays:
public int[] readInts (int length, boolean optimizePositive) throws KryoException {
return readInts(length, length, optimizePositive);
}

/** Reads an int array in bulk using fixed or variable length encoding, depending on
* {@link #setVariableLengthEncoding(boolean)}. This may be more efficient than reading them individually.
* It also takes optimizedLength when {@link Kryo#isOptimizePrimitiveArrays()} is set */
public int[] readInts (int length, int optimizedLength, boolean optimizePositive) throws KryoException {
if (varEncoding) {
int[] array = new int[length];
for (int i = 0; i < optimizedLength; i++)
array[i] = readVarInt(optimizePositive);
return array;
}
return readInts(length, optimizedLength);
}

/** Reads an int array in bulk. This may be more efficient than reading them individually. */
public int[] readInts (int length) throws KryoException {
public int[] readInts (int length, int optimizedLength) throws KryoException {
int[] array = new int[length];
if (optional(length << 2) == length << 2) {
if (optional(optimizedLength << 2) == optimizedLength << 2) {
byte[] buffer = this.buffer;
int p = this.position;
for (int i = 0; i < length; i++, p += 4) {
for (int i = 0; i < optimizedLength; i++, p += 4) {
array[i] = buffer[p] & 0xFF //
| (buffer[p + 1] & 0xFF) << 8 //
| (buffer[p + 2] & 0xFF) << 16 //
| (buffer[p + 3] & 0xFF) << 24;
}
position = p;
} else {
for (int i = 0; i < length; i++)
for (int i = 0; i < optimizedLength; i++)
array[i] = readInt();
}
return array;
}

/** Reads an int array in bulk using fixed or variable length encoding, depending on
* {@link #setVariableLengthEncoding(boolean)}. This may be more efficient than reading them individually. */
public int[] readInts (int length, boolean optimizePositive) throws KryoException {
if (varEncoding) {
int[] array = new int[length];
for (int i = 0; i < length; i++)
array[i] = readVarInt(optimizePositive);
return array;
}
return readInts(length);
}

/** Reads a long array in bulk. This may be more efficient than reading them individually. */
public long[] readLongs (int length) throws KryoException {
long[] array = new long[length];
Expand Down
Expand Up @@ -71,13 +71,27 @@ public void write (Kryo kryo, Output output, int[] object) {
return;
}
output.writeVarInt(object.length + 1, true);
output.writeInts(object, 0, object.length, false);
if(kryo.isOptimizePrimitiveArrays()){
int optimizedSize = object.length;
while(object[optimizedSize-1]==0){
optimizedSize--;
}
output.writeVarInt(optimizedSize + 1, true);
output.writeInts(object, 0, optimizedSize, false);
}else{
output.writeInts(object, 0, object.length, false);
}
}

public int[] read (Kryo kryo, Input input, Class type) {
int length = input.readVarInt(true);
if (length == NULL) return null;
return input.readInts(length - 1, false);
if(kryo.isOptimizePrimitiveArrays()){
int optimizedSize = input.readVarInt(true);
return input.readInts(length-1, optimizedSize - 1, false);
}else{
return input.readInts(length - 1, false);
}
}

public int[] copy (Kryo kryo, int[] original) {
Expand Down
Expand Up @@ -32,6 +32,23 @@ class ArraySerializerTest extends KryoTestCase {
supportsCopy = true;
}

@Test
void testOptimizedIntArrays () {
kryo.register(int[].class);

// default case
roundTrip(6, new int[] {1, 2, 3, 4});
// case where lot of trailing values are default value
// case withbut any optimization, it would have length of 18
roundTrip(18, new int[] {1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});

// case with optimization, it would have length of 7, and also regardless of the size now.
kryo.setOptimizePrimitiveArrays(true);
roundTrip(7, new int[] {1, 2, 3, 4, 0, 0, 0, 0, 0});
roundTrip(7, new int[] {1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});

}

@Test
void testArrays () {
kryo.register(int[].class);
Expand Down