Skip to content

Commit 757cc5a

Browse files
committed
GH-5442 Optimize Varint by replacing multiple put calls with putShort/putInt
1 parent c04a0ae commit 757cc5a

File tree

2 files changed

+93
-46
lines changed

2 files changed

+93
-46
lines changed

core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/Varint.java

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.eclipse.rdf4j.sail.lmdb;
1212

1313
import java.nio.ByteBuffer;
14+
import java.nio.ByteOrder;
1415

1516
/**
1617
* Encodes and decodes unsigned values using variable-length encoding.
@@ -89,26 +90,54 @@ private Varint() {
8990
* @param value value to encode
9091
*/
9192
public static void writeUnsigned(final ByteBuffer bb, final long value) {
93+
if (bb.order() == ByteOrder.BIG_ENDIAN) {
94+
writeUnsignedBigEndian(bb, value);
95+
} else {
96+
writeUnsignedLittleEndian(bb, value);
97+
}
98+
}
99+
100+
private static void writeUnsignedBigEndian(final ByteBuffer bb, final long value) {
101+
if (value <= 240) {
102+
bb.put((byte) value);
103+
} else if (value <= 2287) {
104+
int v = (int) (value - 240);
105+
bb.putShort((short) (((v >>> 8) + 241) << 8 | (v & 0xFF)));
106+
} else if (value <= 67823) {
107+
int v = (int) (value - 2288);
108+
bb.put((byte) 249);
109+
bb.putShort((short) ((v >>> 8) << 8 | (v & 0xFF)));
110+
} else {
111+
int bytes = descriptor(value) + 1;
112+
writeSignificantBitsBigEndian(bb, value, bytes);
113+
}
114+
}
115+
116+
private static void writeUnsignedLittleEndian(final ByteBuffer bb, final long value) {
92117
if (value <= 240) {
93118
bb.put((byte) value);
94119
} else if (value <= 2287) {
95-
bb.put((byte) ((value - 240) / 256 + 241));
96-
bb.put((byte) ((value - 240) % 256));
120+
int v = (int) (value - 240);
121+
bb.putShort((short) ((v & 0xFF) << 8 | ((v >>> 8) + 241)));
97122
} else if (value <= 67823) {
123+
int v = (int) (value - 2288);
98124
bb.put((byte) 249);
99-
bb.put((byte) ((value - 2288) / 256));
100-
bb.put((byte) ((value - 2288) % 256));
125+
bb.putShort((short) ((v & 0xFF) << 8 | (v >>> 8)));
101126
} else {
102127
int bytes = descriptor(value) + 1;
103-
bb.put((byte) (250 + (bytes - 3)));
104-
writeSignificantBits(bb, value, bytes);
128+
bb.order(ByteOrder.BIG_ENDIAN);
129+
try {
130+
writeSignificantBitsBigEndian(bb, value, bytes);
131+
} finally {
132+
bb.order(ByteOrder.LITTLE_ENDIAN);
133+
}
105134
}
106135
}
107136

108137
/**
109138
* Calculates required length in bytes to encode the given long value using variable-length encoding.
110139
*
111-
* @param value the value value
140+
* @param value the value
112141
* @return length in bytes
113142
*/
114143
public static int calcLengthUnsigned(long value) {
@@ -245,22 +274,8 @@ public static long readListElementUnsigned(ByteBuffer bb, int index) {
245274
* @param values array with values to write
246275
*/
247276
public static void writeListUnsigned(final ByteBuffer bb, final long[] values) {
248-
for (int i = 0; i < values.length; i++) {
249-
final long value = values[i];
250-
if (value <= 240) {
251-
bb.put((byte) value);
252-
} else if (value <= 2287) {
253-
bb.put((byte) ((value - 240) / 256 + 241));
254-
bb.put((byte) ((value - 240) % 256));
255-
} else if (value <= 67823) {
256-
bb.put((byte) 249);
257-
bb.put((byte) ((value - 2288) / 256));
258-
bb.put((byte) ((value - 2288) % 256));
259-
} else {
260-
int bytes = descriptor(value) + 1;
261-
bb.put((byte) (250 + (bytes - 3)));
262-
writeSignificantBits(bb, value, bytes);
263-
}
277+
for (final long value : values) {
278+
writeUnsigned(bb, value);
264279
}
265280
}
266281

@@ -296,9 +311,35 @@ public static void readListUnsigned(ByteBuffer bb, int[] indexMap, long[] values
296311
* @param value value to encode
297312
* @param bytes number of significant bytes
298313
*/
299-
private static void writeSignificantBits(ByteBuffer bb, long value, int bytes) {
300-
while (bytes-- > 0) {
301-
bb.put((byte) (0xFF & (value >>> (bytes * 8))));
314+
private static void writeSignificantBitsBigEndian(ByteBuffer bb, long value, int bytes) {
315+
switch (bytes) {
316+
case 3:
317+
bb.putInt((int) ((((250 << 8) | ((value >>> 16) & 0xFF)) << 16) | (value & 0xFFFF)));
318+
break;
319+
case 4:
320+
bb.put((byte) 251); // 250 + (4 - 3)
321+
bb.putInt((int) (value & 0xFFFFFFFFL));
322+
break;
323+
case 5:
324+
bb.putShort((short) ((252 << 8) | ((value >>> 32) & 0xFF)));
325+
bb.putInt((int) (value & 0xFFFFFFFFL));
326+
break;
327+
case 6:
328+
bb.put((byte) 253);
329+
bb.putShort((short) ((value >>> 32) & 0xFFFF));
330+
bb.putInt((int) (value & 0xFFFFFFFFL));
331+
break;
332+
case 7:
333+
bb.putLong(((254 << 8 | ((value >>> 48) & 0xFF)) << 48) | (((value >>> 32) & 0xFFFF) << 32)
334+
| (value & 0xFFFFFFFFL));
335+
break;
336+
case 8:
337+
bb.put((byte) 255);
338+
bb.putLong(value);
339+
break;
340+
default:
341+
throw new IllegalArgumentException(
342+
"Invalid number of bytes " + bytes + " for value " + value + " (must be 3..8)");
302343
}
303344
}
304345

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/VarintTest.java

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,50 @@
1010
*******************************************************************************/
1111
package org.eclipse.rdf4j.sail.lmdb;
1212

13-
import static org.junit.Assert.assertArrayEquals;
14-
import static org.junit.Assert.assertEquals;
15-
1613
import java.nio.ByteBuffer;
14+
import java.nio.ByteOrder;
1715

16+
import org.junit.jupiter.api.Assertions;
1817
import org.junit.jupiter.api.Test;
1918

2019
public class VarintTest {
2120

21+
private final ByteOrder[] byteOrders = new ByteOrder[] { ByteOrder.BIG_ENDIAN, ByteOrder.LITTLE_ENDIAN };
22+
2223
long[] values = new long[] {
2324
240, 2287, 67823, 16777215, 4294967295L, 1099511627775L, 281474976710655L, 72057594037927935L,
2425
72057594037927935L + 1
2526
};
2627

2728
@Test
2829
public void testVarint() {
29-
ByteBuffer bb = ByteBuffer.allocate(9);
30-
for (int i = 0; i < values.length; i++) {
31-
bb.clear();
32-
Varint.writeUnsigned(bb, values[i]);
33-
bb.flip();
34-
assertEquals("Encoding should use " + (i + 1) + " bytes", i + 1, bb.remaining());
35-
assertEquals("Encoded and decoded value should be equal", values[i], Varint.readUnsigned(bb));
30+
for (ByteOrder order : byteOrders) {
31+
ByteBuffer bb = ByteBuffer.allocate(9).order(order);
32+
for (int i = 0; i < values.length; i++) {
33+
bb.clear();
34+
Varint.writeUnsigned(bb, values[i]);
35+
bb.flip();
36+
Assertions.assertEquals(i + 1, bb.remaining(), "Encoding should use " + (i + 1) + " bytes");
37+
Assertions.assertEquals(values[i], Varint.readUnsigned(bb),
38+
"Encoded and decoded value should be equal");
39+
}
3640
}
3741
}
3842

3943
@Test
4044
public void testVarintList() {
41-
ByteBuffer bb = ByteBuffer.allocate(2 + 4 * Long.BYTES);
42-
for (int i = 0; i < values.length - 4; i++) {
43-
long[] expected = new long[4];
44-
System.arraycopy(values, 0, expected, 0, 4);
45-
bb.clear();
46-
Varint.writeListUnsigned(bb, expected);
47-
bb.flip();
48-
long[] actual = new long[4];
49-
Varint.readListUnsigned(bb, actual);
50-
assertArrayEquals("Encoded and decoded value should be equal", expected, actual);
45+
for (ByteOrder order : byteOrders) {
46+
ByteBuffer bb = ByteBuffer.allocate(2 + 4 * Long.BYTES).order(order);
47+
for (int i = 0; i < values.length - 4; i++) {
48+
long[] expected = new long[4];
49+
System.arraycopy(values, 0, expected, 0, 4);
50+
bb.clear();
51+
Varint.writeListUnsigned(bb, expected);
52+
bb.flip();
53+
long[] actual = new long[4];
54+
Varint.readListUnsigned(bb, actual);
55+
Assertions.assertArrayEquals(expected, actual, "Encoded and decoded value should be equal");
56+
}
5157
}
5258
}
5359
}

0 commit comments

Comments
 (0)