-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch from sun.misc.Unsafe to java.nio.ByteBuffer for vectoer (de-)s…
…erialization (forwards and backwards compatible) (#608)
- Loading branch information
1 parent
151a96c
commit d39e53c
Showing
22 changed files
with
452 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
|Model|Parameters|Recall|Queries per Second| | ||
|---|---|---|---| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=500 probes=0|0.379|353.162| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=1000 probes=0|0.447|295.007| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=500 probes=3|0.634|286.531| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=1000 probes=3|0.716|245.690| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=500 probes=0|0.767|312.826| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=1000 probes=0|0.846|265.204| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=500 probes=3|0.921|221.817| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=1000 probes=3|0.960|195.653| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=500 probes=0|0.378|349.851| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=1000 probes=0|0.446|296.219| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=500 probes=3|0.635|286.468| | ||
|eknn-l2lsh|L=100 k=4 w=1024 candidates=1000 probes=3|0.716|244.536| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=500 probes=0|0.767|315.023| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=1000 probes=0|0.847|264.479| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=500 probes=3|0.922|220.714| | ||
|eknn-l2lsh|L=100 k=4 w=2048 candidates=1000 probes=3|0.960|193.597| |
146 changes: 146 additions & 0 deletions
146
...ks/src/main/scala/com/klibisz/elastiknn/jmhbenchmarks/VectorSerializationBenchmarks.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package com.klibisz.elastiknn.jmhbenchmarks | ||
|
||
import com.klibisz.elastiknn.storage.{ByteBufferSerialization, UnsafeSerialization} | ||
import org.openjdk.jmh.annotations._ | ||
|
||
import scala.util.Random | ||
|
||
@State(Scope.Benchmark) | ||
class VectorSerializationBenchmarksState { | ||
implicit private val rng: Random = new Random(0) | ||
val floatArray = (0 until 1000).map(_ => rng.nextFloat()).toArray | ||
val floatArraySerialized = ByteBufferSerialization.writeFloats(floatArray) | ||
val intArray = (0 until 1000).map(_ => rng.nextInt()).toArray | ||
val intArraySerialized = ByteBufferSerialization.writeInts(intArray) | ||
val ints = Array(Int.MinValue + 1, Short.MinValue + 1, Byte.MinValue + 1, 0, Byte.MaxValue - 1, Short.MaxValue - 1, Int.MaxValue - 1) | ||
val intsSerialized = ints.map(ByteBufferSerialization.writeInt) | ||
} | ||
|
||
class VectorSerializationBenchmarks { | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeFloats_Unsafe(state: VectorSerializationBenchmarksState): Array[Byte] = { | ||
UnsafeSerialization.writeFloats(state.floatArray) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeFloats_ByteBuffer(state: VectorSerializationBenchmarksState): Array[Byte] = { | ||
ByteBufferSerialization.writeFloats(state.floatArray) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def readFloats_Unsafe(state: VectorSerializationBenchmarksState): Array[Float] = { | ||
UnsafeSerialization.readFloats(state.floatArraySerialized, 0, state.floatArraySerialized.length) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def readFloats_ByteBuffer(state: VectorSerializationBenchmarksState): Array[Float] = { | ||
ByteBufferSerialization.readFloats(state.floatArraySerialized, 0, state.floatArraySerialized.length) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeInts_Unsafe(state: VectorSerializationBenchmarksState): Array[Byte] = { | ||
UnsafeSerialization.writeInts(state.intArray) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeInts_ByteBuffer(state: VectorSerializationBenchmarksState): Array[Byte] = { | ||
ByteBufferSerialization.writeInts(state.intArray) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def readInts_Unsafe(state: VectorSerializationBenchmarksState): Array[Int] = { | ||
UnsafeSerialization.readInts(state.intArraySerialized, 0, state.intArraySerialized.length) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def readInts_ByteBuffer(state: VectorSerializationBenchmarksState): Array[Int] = { | ||
ByteBufferSerialization.readInts(state.intArraySerialized, 0, state.intArraySerialized.length) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeIntsWithPrefix_Unsafe(state: VectorSerializationBenchmarksState): Array[Byte] = { | ||
UnsafeSerialization.writeIntsWithPrefix(state.intArray.length, state.intArray) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeIntsWithPrefix_ByteBuffer(state: VectorSerializationBenchmarksState): Array[Byte] = { | ||
ByteBufferSerialization.writeIntsWithPrefix(state.intArray.length, state.intArray) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeInt_Unsafe(state: VectorSerializationBenchmarksState): Unit = { | ||
state.ints.foreach(UnsafeSerialization.writeInt) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def writeInt_ByteBuffer(state: VectorSerializationBenchmarksState): Unit = { | ||
state.ints.foreach(ByteBufferSerialization.writeInt) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def readInt_Unsafe(state: VectorSerializationBenchmarksState): Unit = { | ||
state.intsSerialized.foreach(UnsafeSerialization.readInt) | ||
} | ||
|
||
@Benchmark | ||
@BenchmarkMode(Array(Mode.Throughput)) | ||
@Fork(value = 1) | ||
@Warmup(time = 5, iterations = 1) | ||
@Measurement(time = 5, iterations = 1) | ||
def readInt_ByteBuffer(state: VectorSerializationBenchmarksState): Unit = { | ||
state.intsSerialized.foreach(ByteBufferSerialization.readInt) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
elastiknn-models/src/main/java/com/klibisz/elastiknn/storage/ByteBufferSerialization.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package com.klibisz.elastiknn.storage; | ||
|
||
import scala.util.control.Exception; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.nio.ByteOrder; | ||
|
||
public class ByteBufferSerialization { | ||
public static final int numBytesInInt = 4; | ||
public static final int numBytesInFloat = 4; | ||
|
||
public static final ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN; | ||
|
||
public static byte[] writeInt(final int i) { | ||
final int a = Math.abs(i); | ||
if (a <= Byte.MAX_VALUE) { | ||
return new byte[]{(byte) i}; | ||
} else if (a <= Short.MAX_VALUE) { | ||
return new byte[]{ | ||
(byte) (i & 0xFF), | ||
(byte) ((i >> 8) & 0xFF) | ||
}; | ||
} else { | ||
return new byte[]{ | ||
(byte) (i & 0xFF), | ||
(byte) ((i >> 8) & 0xFF), | ||
(byte) ((i >> 16) & 0xFF), | ||
(byte) ((i >> 24) & 0xFF), | ||
}; | ||
} | ||
} | ||
|
||
public static int readInt(final byte[] barr) { | ||
if (barr.length == 1) { | ||
return barr[0]; | ||
} else if (barr.length == 2) { | ||
ByteBuffer bb = ByteBuffer.wrap(barr).order(byteOrder); | ||
return bb.getShort(); | ||
} else { | ||
ByteBuffer bb = ByteBuffer.wrap(barr).order(byteOrder); | ||
return bb.getInt(); | ||
} | ||
} | ||
|
||
public static byte[] writeInts(final int[] iarr) { | ||
ByteBuffer bb = ByteBuffer.allocate(iarr.length * numBytesInInt).order(byteOrder); | ||
bb.asIntBuffer().put(iarr); | ||
return bb.array(); | ||
} | ||
|
||
public static byte[] writeIntsWithPrefix(int prefix, final int[] iarr) { | ||
ByteBuffer bb = ByteBuffer.allocate((iarr.length + 1) * numBytesInInt).order(byteOrder); | ||
bb.asIntBuffer().put(prefix).position(1).put(iarr); | ||
return bb.array(); | ||
} | ||
|
||
public static int[] readInts(final byte[] barr, final int offset, final int length) { | ||
int[] dst = new int[length / numBytesInInt]; | ||
ByteBuffer bb = ByteBuffer.wrap(barr, offset, length).order(byteOrder); | ||
bb.asIntBuffer().get(dst); | ||
return dst; | ||
} | ||
|
||
public static byte[] writeFloats(final float[] farr) { | ||
ByteBuffer bb = ByteBuffer.allocate(farr.length * numBytesInFloat).order(byteOrder); | ||
bb.asFloatBuffer().put(farr); | ||
return bb.array(); | ||
} | ||
|
||
public static float[] readFloats(final byte[] barr, int offset, int length) { | ||
float[] dst = new float[length / numBytesInFloat]; | ||
ByteBuffer bb = ByteBuffer.wrap(barr, offset, length).order(byteOrder); | ||
bb.asFloatBuffer().get(dst); | ||
return dst; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.