Skip to content

Commit 38cdce2

Browse files
committed
perf(BitpackedCodec): optimize unpackLoop64 hot path
Hoist per-lane bounds check out of inner loop by detecting full blocks (all 1024 indices in-range) once per block. Split remainingBits branch to row level. Replace lane*8 multiplies with additive laneOff stride. 44 → 84 ops/s on javaReadVolume (10M rows, I64 column).
1 parent 2ed02f6 commit 38cdce2

1 file changed

Lines changed: 39 additions & 15 deletions

File tree

core/src/main/java/io/github/dfa1/vortex/encoding/BitpackedCodec.java

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ private static void unpackLoop64(MemorySegment buf, int bitWidth, int offset, lo
237237
long blockByteStride = 128L * bitWidth;
238238
for (int block = 0; block < blockCount; block++, blockByteOff += blockByteStride) {
239239
int blockLogicStart = block * 1024 - offset;
240+
boolean fullBlock = blockLogicStart >= 0 && (long) blockLogicStart + 1023L < rowCount;
241+
240242
for (int row = 0; row < 64; row++) {
241243
int currWord = (row * bitWidth) / 64;
242244
int nextWord = ((row + 1) * bitWidth) / 64;
@@ -247,24 +249,46 @@ private static void unpackLoop64(MemorySegment buf, int bitWidth, int offset, lo
247249
int s = row % 8;
248250
int baseIdx = blockLogicStart + FL_ORDER[o] * 16 + s * 128;
249251
long wordBase = blockByteOff + (long) lanes * currWord * 8;
250-
long hiBase = (remainingBits > 0) ? blockByteOff + (long) lanes * nextWord * 8 : 0L;
251-
long loMask = (remainingBits > 0) ? (1L << currentBits) - 1L : 0L;
252-
long hiMask = (remainingBits > 0) ? (1L << remainingBits) - 1L : 0L;
253-
for (int lane = 0; lane < lanes; lane++) {
254-
int logicalIdx = baseIdx + lane;
255-
if (logicalIdx < 0 || logicalIdx >= rowCount) {
256-
continue;
257-
}
258-
long src = buf.get(LE_LONG, wordBase + (long) lane * 8);
259-
long value;
252+
253+
if (fullBlock) {
254+
long outBase = (long) baseIdx * 8;
260255
if (remainingBits > 0) {
261-
long lo = (src >>> shift) & loMask;
262-
long hi = buf.get(LE_LONG, hiBase + (long) lane * 8) & hiMask;
263-
value = lo | (hi << currentBits);
256+
long hiBase = blockByteOff + (long) lanes * nextWord * 8;
257+
long loMask = (1L << currentBits) - 1L;
258+
long hiMask = (1L << remainingBits) - 1L;
259+
long laneOff = 0L;
260+
for (int lane = 0; lane < lanes; lane++, laneOff += 8L) {
261+
long lo = (buf.get(LE_LONG, wordBase + laneOff) >>> shift) & loMask;
262+
long hi = buf.get(LE_LONG, hiBase + laneOff) & hiMask;
263+
out.set(LE_LONG, outBase + laneOff, lo | (hi << currentBits));
264+
}
264265
} else {
265-
value = (src >>> shift) & bitMask;
266+
long laneOff = 0L;
267+
for (int lane = 0; lane < lanes; lane++, laneOff += 8L) {
268+
out.set(LE_LONG, outBase + laneOff,
269+
(buf.get(LE_LONG, wordBase + laneOff) >>> shift) & bitMask);
270+
}
271+
}
272+
} else {
273+
long hiBase = (remainingBits > 0) ? blockByteOff + (long) lanes * nextWord * 8 : 0L;
274+
long loMask = (remainingBits > 0) ? (1L << currentBits) - 1L : 0L;
275+
long hiMask = (remainingBits > 0) ? (1L << remainingBits) - 1L : 0L;
276+
for (int lane = 0; lane < lanes; lane++) {
277+
int logicalIdx = baseIdx + lane;
278+
if (logicalIdx < 0 || logicalIdx >= rowCount) {
279+
continue;
280+
}
281+
long src = buf.get(LE_LONG, wordBase + (long) lane * 8);
282+
long value;
283+
if (remainingBits > 0) {
284+
long lo = (src >>> shift) & loMask;
285+
long hi = buf.get(LE_LONG, hiBase + (long) lane * 8) & hiMask;
286+
value = lo | (hi << currentBits);
287+
} else {
288+
value = (src >>> shift) & bitMask;
289+
}
290+
out.set(LE_LONG, (long) logicalIdx * 8, value);
266291
}
267-
out.set(LE_LONG, (long) logicalIdx * 8, value);
268292
}
269293
}
270294
}

0 commit comments

Comments
 (0)