From 499cf421dd32d684e98b54edd382ba807e868ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Szathma=CC=81ry?= Date: Sat, 26 Dec 2015 11:03:58 +0100 Subject: [PATCH] Inline and optimize some decoding methods to improve performance Inlined and optimized sizeFromCtrlByte and decodePointer. Reduces object churn on return values and also improves JIT inlining patterns. Net speedup on benchmark 10-15%. --- src/main/java/com/maxmind/db/Decoder.java | 65 ++++++++++------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/maxmind/db/Decoder.java b/src/main/java/com/maxmind/db/Decoder.java index a626ddbb..68a17dbf 100644 --- a/src/main/java/com/maxmind/db/Decoder.java +++ b/src/main/java/com/maxmind/db/Decoder.java @@ -22,6 +22,8 @@ final class Decoder { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final int[] POINTER_VALUE_OFFSETS = { 0, 0, 1 << 11, (1 << 19) + ((1) << 11), 0 }; + // XXX - This is only for unit testings. We should possibly make a // constructor to set this boolean POINTER_TEST_HACK = false; @@ -99,14 +101,19 @@ Result decode(int offset) throws IOException { // use the size to determine the length of the pointer and then follow // it. if (type.equals(Type.POINTER)) { - Result pointer = this.decodePointer(ctrlByte, offset); + int pointerSize = ((ctrlByte >>> 3) & 0x3) + 1; + int base = pointerSize == 4 ? (byte) 0 : (byte) (ctrlByte & 0x7); + int packed = this.decodeInteger(base, pointerSize); + long pointer = packed + this.pointerBase + POINTER_VALUE_OFFSETS[pointerSize]; + int newOffset = offset + pointerSize; // for unit testing if (this.POINTER_TEST_HACK) { - return pointer; + return new Result(new LongNode(pointer), newOffset); } - Result result = this.decode((pointer.getNode().asInt())); - result.setOffset(pointer.getOffset()); + + Result result = this.decode((int) pointer); + result.setOffset(newOffset); return result; } @@ -126,9 +133,22 @@ Result decode(int offset) throws IOException { offset++; } - int[] sizeArray = this.sizeFromCtrlByte(ctrlByte, offset); - int size = sizeArray[0]; - offset = sizeArray[1]; + int size = ctrlByte & 0x1f; + if (size >= 29) { + int bytesToRead = size - 28; + int i = this.decodeInteger(bytesToRead); + switch (size) { + case 29: + size = 29 + i; + break; + case 30: + size = 285 + i; + break; + default: + size = 65821 + (i & (0x0FFFFFFF >>> 32 - 8 * bytesToRead)); + } + offset += bytesToRead; + } return this.decodeByType(type, offset, size); } @@ -176,19 +196,6 @@ private Result decodeByType(Type type, int offset, int size) } } - private static final int[] POINTER_VALUE_OFFSETS = {0, 0, 1 << 11, - (1 << 19) + ((1) << 11), 0}; - - private Result decodePointer(int ctrlByte, int offset) { - int pointerSize = ((ctrlByte >>> 3) & 0x3) + 1; - int base = pointerSize == 4 ? (byte) 0 : (byte) (ctrlByte & 0x7); - int packed = this.decodeInteger(base, pointerSize); - long pointer = packed + this.pointerBase - + POINTER_VALUE_OFFSETS[pointerSize]; - - return new Result(new LongNode(pointer), offset + pointerSize); - } - private String decodeString(int size) throws CharacterCodingException { int oldLimit = buffer.limit(); buffer.limit(buffer.position() + size); @@ -300,24 +307,6 @@ private Result decodeMap(int size, int offset) throws IOException { return new Result(map, offset); } - private int[] sizeFromCtrlByte(int ctrlByte, int offset) { - int size = ctrlByte & 0x1f; - int bytesToRead = size < 29 ? 0 : size - 28; - - if (size == 29) { - int i = this.decodeInteger(bytesToRead); - size = 29 + i; - } else if (size == 30) { - int i = this.decodeInteger(bytesToRead); - size = 285 + i; - } else if (size > 30) { - int i = this.decodeInteger(bytesToRead) - & (0x0FFFFFFF >>> (32 - (8 * bytesToRead))); - size = 65821 + i; - } - return new int[]{size, offset + bytesToRead}; - } - private byte[] getByteArray(int length) { return Decoder.getByteArray(this.buffer, length); }