Home > Mobile >  Fastest way to read a 32-bit integer from an offset in a byte array
Fastest way to read a 32-bit integer from an offset in a byte array

Time:04-09

Previous discussions on reading an integer from a byte array in Java, focus on the scenario where what you have is four bytes. I have a slightly different scenario:

  • A fixed array of two billion bytes.

  • Input: a random offset into that array. (Though hopefully sufficiently nonrandom to have a reasonably high cache hit rate.)

  • This operation will happen frequently, such that it needs to run as fast as possible. The ideal would be if there is an idiom that the JIT compiler can recognize and compile into an unaligned load instruction, if the CPU supports such. (Every mainstream CPU pays for unaligned support in every memory access, even in the typical case when it's not used. Might as well take advantage of it, this one time when it would be useful.)

What's the the fastest way to perform this operation? Obviously I can just write the read-shift loop by hand, but is there a faster idiom? Or if it is to be done by hand, which variant generates the fastest code?

I'm on OpenJDK 17, if that matters.

CodePudding user response:

In Java 17, the jdk.internal.misc.Unsafe class provides a couple of getIntUnaligned methods that can be used to fetch an int from an arbitrary offset in (for example) a byte array. Read the comments in the source code for an explanation on how to use it.

The methods are marked as @IntrinsicCandidate, so the JIT compiler knows how to optimize them. If this works (see below!!) it should be fastest. (Or equal fastest.)


However there are some important caveats:

  1. Unaligned fetches are not supported on all platforms. The Unsafe.unalignedAccess() method tells you if the platform is capable.

  2. This is not the "normal" Unsafe class. That is sun.misc.Unsafe ... and it is still present on Java 17.

  3. I think that access to jdk.internal.misc.Unsafe is going to be problematic ... because it is a JDK internal class and access to such classes from regular applications is blocked from Java 16 onward. (I don't know if there is a way around the block ...)

  4. The normal caveat that internal APIs may change without notice applies.


There are other accessors in Unsafe but they generally assume that the access is aligned according to the type of the value you are fetching. Your use-case does not allow you to make that assumption.

I doubt that the JIT compiler would be able to figure out that a certain sequence of bytecodes that perform byte fetches, shifts and bitwise operations on a byte array is actually fetching an int. Or determine that it is not word aligned so that it can optimize to a non-aligned fetch instruction.

CodePudding user response:

If you do not can or wish to use internal classes, you can use a VarHandle to access this:

private static final VarHandle READ_ARRAY = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder());

public static void main(String[] args) {
    byte[] arr = ...;
    
    int pos = ...; // pos is the index into the byte array, and may be unaligned.
    int result = (int) READ_ARRAY.get(arr, pos);


    System.out.println(result);
}

While this adds some indirection, ultimately it will call Unsafe.unalignedAccess() when supported.

  • Related