Good day to all!
The task is this: there is an array of byte [] , received over the network. In fact, these bytes contain an array of int [] of the same size, four times smaller. You just need to take the memory area and change the pointer by rewriting the service information about the array (length, etc.). Arrays are relatively large ~ 100kb. There are many arrays and it is critical to process them quickly.
Tested solutions and speeds (with JIT warming up):
- Bypassing the loop and collecting int through bitwise operations (was dropped immediately due to inefficiency)
- Using ByteBuffer.wrap (byte []). AsIntBuffer (). Get (int []) time: 49200 nanoseconds
- Using JNI and GetByteArrayRegion / SetIntArrayRegion Time: 14700 nanoseconds
- Unsafe closed API time: about 200 nanoseconds on average
As a result of the research, it turned out that the most interesting option is castes through the Unsafe closed API, however there are some problems related either to caching, or to the fact that I do not write all the information correctly in the header of arrays. Please help to figure out if anyone knows, or who has a desire =)
public class UnsafeUtil { public static Unsafe unsafe; private static final long INT_ARRAY_HEADER_OFFSET; private static final long BYTE_ARRAY_HEADER_OFFSET; private static final long ADDRESS_SIZE; private static final long ARRAY_TYPE_OFFSET; private static final long ARRAY_LENGTH_OFFSET; private static final int ARRAY_TYPE_INTS; private static final int ARRAY_TYPE_BYTES; private static final int ARRAY_LENGTH_INTS = 32768; private static final int ARRAY_LENGTH_BYTES = ARRAY_LENGTH_INTS<<2; private static final long ARRAY_HEADER_INTS; private static final long ARRAY_HEADER_BYTES; private static int[] test; private static UnsafeUtil instance = new UnsafeUtil(); static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe)f.get(null); int[] ints = new int[ARRAY_LENGTH_INTS]; byte[] bytes = new byte[ARRAY_LENGTH_BYTES]; ADDRESS_SIZE = unsafe.addressSize(); BYTE_ARRAY_HEADER_OFFSET = unsafe.arrayBaseOffset(ints.getClass()); INT_ARRAY_HEADER_OFFSET = unsafe.arrayBaseOffset(bytes.getClass()); System.err.println("INFO: Initialized binary caster."); System.err.println("INFO: address size " + ADDRESS_SIZE); System.err.println("INFO: offset int[] " + INT_ARRAY_HEADER_OFFSET); System.err.println("INFO: offset byte[] " + BYTE_ARRAY_HEADER_OFFSET); if (BYTE_ARRAY_HEADER_OFFSET != INT_ARRAY_HEADER_OFFSET){ System.err.println("CRITICAL ERROR: Sorry, i don't know your environment. It seems" + " like you are using something rare (int[] offset != byte[] offset, i dont know" + "how to cast it fast in binary level.)"); System.exit(1); } // searching length offset ARRAY_TYPE_OFFSET = ADDRESS_SIZE; ARRAY_LENGTH_OFFSET = ADDRESS_SIZE + 4L; ARRAY_TYPE_BYTES = unsafe.getInt(bytes, ARRAY_TYPE_OFFSET); ARRAY_TYPE_INTS = unsafe.getInt(ints, ARRAY_TYPE_OFFSET); ARRAY_HEADER_BYTES = unsafe.getLong(bytes, ADDRESS_SIZE); ARRAY_HEADER_INTS = unsafe.getLong(ints, ADDRESS_SIZE); // warming-up long l = 0L; for (int i = 0; i < 10000; i++){ l+= testReinterpretBtI(); } for (int i = 0; i < 10000; i++){ l+= testReinterpretItB(); } } catch (Exception ex) { throw new RuntimeException(ex); } } public static void main(String[] args) { long l = 0; for (int i = 0; i < 10; i++){ l+= testReinterpretBtI(); } for (int i = 0; i < 10; i++){ l+= testReinterpretItB(); } System.err.println("AVG CAST TIME WITH WARM-UP: " + (l/20) + " ns"); } public static byte[] modifyArrayLength(byte[] array, int newLength, long lengthOffset){ unsafe.putInt(array, lengthOffset, newLength); return array; } public static int[] reinterpretAsIntArray(byte[] array){ unsafe.putLong(array, ADDRESS_SIZE, ARRAY_HEADER_INTS); Object o = array; return (int[]) o; } public static byte[] reinterpretAsByteArray(int[] array){ unsafe.putLong(array, ADDRESS_SIZE, ARRAY_HEADER_BYTES); Object o = array; return (byte[]) o; } private static long testReinterpretBtI(){ byte[] b = new byte[ARRAY_LENGTH_BYTES]; long t1 = System.nanoTime(); int[] i = reinterpretAsIntArray(b); long t2 = System.nanoTime(); test = i; return (t2-t1); } private static long testReinterpretItB(){ int[] i = new int[ARRAY_LENGTH_INTS]; long t1 = System.nanoTime(); byte[] b = reinterpretAsByteArray(i); long t2 = System.nanoTime(); test = i; return (t2-t1); } } UPDATE: It was possible to find out about where to dig and what the problem is. Probably, when calling unsafe, I overwritten the object's reference counter and at some point the object was deleted by the garbage collector, which then led to an access violation when trying to access the already freed memory. I will think about how to organize the exchange stack so that there are no jambs with links. Probably I will manually count, or I will do +1 to the counter and I will reuse the same objects, or off-heap array. Presumably the last is the best.
UPDATE: There is a Dequeue queue from fixed-size byte [] arrays. The byte [] arrays from the queue are used as buffers for receiving data over the network in order not to do memalloc. The byte [] data coming from the server to the client is placed into the buffers from the queue. The client casts the byte [] buffer data into an int [] array, using it for its own purposes. Caste occurs through unsafe as described in the code above. when int [] arrays are no longer needed, they are again cast into byte [] and sent back to the queue. at some point (it seems, after passing gc) accessViolation happens when trying to access int []
char * arr = ...; return (int *)arr ;char * arr = ...; return (int *)arr ;- pavelByteBuffer.wrap(byte[]).asIntBuffer().get(int[])? it copies one array to another. .get (int []) - This method transfers ints from this buffer to the given destination array. Directly with intbuffer th work impossible? And why not immediately get int []? - Sergey