use more efficient way to determine needed bytes for variable byte

encoding

I experimented with a few branch-free variants, but they were slower
than just using ternary operators and readable code.
This commit is contained in:
2024-04-01 12:44:30 +02:00
parent 6b8e3d2089
commit 452030de5e
2 changed files with 16 additions and 11 deletions

View File

@@ -28,15 +28,10 @@ public class VariableByteEncoder {
public static final long MIN_VALUE = Long.MIN_VALUE / 2 + 1;
public static final long MAX_VALUE = Long.MAX_VALUE / 2;
private static final int MAX_BYTES_PER_VALUE = 10;
private static final int CONTINUATION_BYTE_FLAG = 1 << 7; // 10000000
private static final long DATA_BITS = (1 << 7) - 1; // 01111111
private static final ThreadLocal<byte[]> SINGLE_VALUE_BUFFER = ThreadLocal
.withInitial(() -> new byte[MAX_BYTES_PER_VALUE]);
/**
* Encodes time and value into the given buffer.
* <p>
@@ -137,8 +132,18 @@ public class VariableByteEncoder {
* input: 0 1 -1 2 -2 3 -3
* encoded: 1 2 3 4 5 6 7
* </pre>
*
* Note: 1. I tried to replace this ternary operator with a branchfree
* alternative, but it was slower:
*
* <pre>
* final long sign = (x - 1) >> 63; // will be 111...1 if first bit was 1 (x was negative) and 000..0
* // otherwise
* final long signBit = sign & 0x1; // will be 1 for negative x and 0 otherwise
* return (((x * 2) ^ (sign))) + (signBit * 2); // same as above, but branchless
* </pre>
*/
private static long encodeIntoPositiveValue(final long value) {
static long encodeIntoPositiveValue(final long value) {
return value > 0 ? value * 2 : (value * -2) + 1;
}
@@ -234,9 +239,9 @@ public class VariableByteEncoder {
}
public static int neededBytes(final long value) {
final byte[] buffer = SINGLE_VALUE_BUFFER.get();
final int usedBytes = encodeInto(value, buffer, 0);
return usedBytes;
final long val = encodeIntoPositiveValue(value);
final int numberOfOnes = 64 - Long.numberOfLeadingZeros(val);
return numberOfOnes / 7 + (numberOfOnes % 7 == 0 ? 0 : 1);
}
}

View File

@@ -81,7 +81,7 @@ public class VariableByteEncoderTest {
Assertions.assertEquals(originalValues, decodedValues);
}
public static Stream<Arguments> providerNededBytes() {
public static Stream<Arguments> providerNeededBytes() {
return Stream.of( //
Arguments.of(0, 1), //
Arguments.of(-10, 1), //
@@ -98,7 +98,7 @@ public class VariableByteEncoderTest {
}
@ParameterizedTest
@MethodSource("providerNededBytes")
@MethodSource("providerNeededBytes")
public void testNeededBytes(final long value, final int expectedNeededBytes) {
final int neededBytes = VariableByteEncoder.neededBytes(value);