Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8016539

Optimization / long/int/short/double/float serialization

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 7
    • performance
    • None
    • os_x

      A DESCRIPTION OF THE REQUEST :
      Recently I benchmarked some different implementation to serialize and deserialize long, int, short, double and float primitives to/from byte[ arrays.

      The first (just plain, portable Java code) one simply took the primitive, performed some bit shifting and tansfered the primitive value as multiple byte values to/from the byte array.
      The second one uses sun.misc.Unsafe functionality.
      The third one used (direct) ByteBuffers.

      The test begins with a warmup phase to let the JVM compile byte code to native code (JIT) with JVM option " -XX:CompileThreshold=10 " .
      Next, each implementation is called 10000000 times for each primitive type and for get/set operations.

      The performance results are really interesting (although we are talking about fractions of seconds):

      Serializing using portable Java code is faster than sun.misc.Unsafe#getXYZ (when JIT compiled).
      Deserializing using Usafe is faster than portable Java code (even when JIT compiled).

      Serializing using ByteBuffer.get/putXYZ is 3 to 15 times(!) slower than Java/Unsafe alone.

      The benchmark results were taken using using Java8 - but they are (nearly) the same with Java6 and Java7.

      JUSTIFICATION :
      Please investigate that issue.
      I do not understand why an atomic Unsafe.putXYZ is slower than bit shifting and byte[] by index access.
      Maybe it points out a point for JVM performace optimization.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      I would expect Unsafe.putXYZ() to be faster than " portable Java code " .
      An I would expect ByteBuffer.get/putXYZ not to be that slow.

      ---------- BEGIN SOURCE ----------
      import org.junit.FixMethodOrder;
      import org.junit.Test;
      import org.junit.runners.MethodSorters;
      import sun.misc.Unsafe;

      import java.nio.ByteBuffer;

      /**
       * @author Robert Stupp (last modified by $Author$)
       * @version $Revision$ $Date$
       */
      @FixMethodOrder(MethodSorters.NAME_ASCENDING)
      @SuppressWarnings( " UseOfSystemOutOrSystemErr " )
      public class UnsafesPerformanceDeveloperTest {

          public static final int LOOPS = 10000000;

          static String nanosToTime(long nanos) {
              StringBuilder sb = new StringBuilder(10);

              long ns = nanos % THOUSAND;
              nanos /= THOUSAND;
              long mi = nanos % THOUSAND;
              nanos /= THOUSAND;
              long ms = nanos % THOUSAND;
              nanos /= THOUSAND;
              long seconds = nanos % SECONDS_PER_MINUTE;
              nanos /= SECONDS_PER_MINUTE;
              long minutes = nanos % MINUTES_PER_HOUR;
              nanos /= MINUTES_PER_HOUR;
              long hours = nanos % HOURS_PER_DAY;
              nanos /= HOURS_PER_DAY;
              long days = nanos;

              boolean print = days > 0;
              if (print)
                  sb.append(days).append('d');

              boolean hadPrint = print;
              print |= hours > 0;
              if (print) {
                  if (hadPrint && hours < 10)
                      sb.append('0');
                  sb.append(hours).append('h');
              }

              hadPrint = print;
              print |= minutes > 0;
              if (print) {
                  if (hadPrint && minutes < 10)
                      sb.append('0');
                  sb.append(minutes).append('m');
              }

              hadPrint = print;
              print |= seconds > 0;
              if (print) {
                  if (hadPrint && seconds < 10)
                      sb.append('0');
                  sb.append(seconds).append('s');
              }

              hadPrint = print;
              print |= ms > 0 || (mi == 0 && ns == 0);
              if (print) {
                  if (hadPrint && ms < 100)
                      sb.append('0');
                  if (hadPrint && ms < 10)
                      sb.append('0');
                  sb.append(ms).append( " ms " );
              }

              if (mi > 0 || ns > 0) {
                  hadPrint = print;
                  print |= mi > 0;
                  if (print) {
                      if (hadPrint && mi < 100)
                          sb.append('0');
                      if (hadPrint && mi < 10)
                          sb.append('0');
                      sb.append(mi).append( " us " );
                  }
              }

              if (ns > 0) {
                  hadPrint = print;
                  // print |= ns > 0;
                  // if (print) {
                  if (hadPrint && ns < 100)
                      sb.append('0');
                  if (hadPrint && ns < 10)
                      sb.append('0');
                  sb.append(ns).append( " ns " );
                  // }
              }

              return sb.toString();
          }

          @Test
          public void aWarmup() throws InterruptedException {
              byte[] arr = new byte[24];
              ByteBuffer bb = ByteBuffer.wrap(arr);

              System.out.println( " Warmup... " );
              for (int warmup = 0; warmup < 20; warmup++) {
                  for (int n = 0; n < 10000000; n++) {
                      serialize(0x1234567812345678L, arr, 0);
                      Unsafes.fastSet(0x1234567812345678L, arr, 0);
                      // Unsafes.Really.fastSet(0x1234567812345678L, arr, 0);
                      bb.clear();
                      bb.putLong(0x1234567812345678L);
                      deserializeLong(arr, 0);
                      Unsafes.fastGetLong(arr, 0);
                      // Unsafes.Really.fastGetLong(arr, 0);
                      bb.flip();
                      bb.getLong();

                      serialize(0x12345678, arr, 0);
                      Unsafes.fastSet(0x12345678, arr, 0);
                      // Unsafes.Really.fastSet(0x12345678, arr, 0);
                      bb.clear();
                      bb.putInt(0x12345678);
                      deserializeInt(arr, 0);
                      Unsafes.fastGetInt(arr, 0);
                      // Unsafes.Really.fastGetLong(arr, 0);
                      bb.flip();
                      bb.getInt();

                      serialize((short) 0x1234, arr, 0);
                      Unsafes.fastSet((short) 0x1234, arr, 0);
                      // Unsafes.Really.fastSet((short)0x1234, arr, 0);
                      bb.clear();
                      bb.putShort((short) 0x1234);
                      deserializeShort(arr, 0);
                      Unsafes.fastGetShort(arr, 0);
                      // Unsafes.Really.fastGetShort(arr, 0);
                      bb.flip();
                      bb.getShort();

                      serialize(11.21378923f, arr, 0);
                      Unsafes.fastSet(11.21378923f, arr, 0);
                      // Unsafes.Really.fastSet(11.21378923f, arr, 0);
                      bb.clear();
                      bb.putFloat(11.21378923f);
                      deserializeFloat(arr, 0);
                      Unsafes.fastGetFloat(arr, 0);
                      // Unsafes.Really.fastGetFloat(arr, 0);
                      bb.flip();
                      bb.getFloat();

                      serialize(11.21378923d, arr, 0);
                      Unsafes.fastSet(11.21378923d, arr, 0);
                      // Unsafes.Really.fastSet(11.21378923d, arr, 0);
                      bb.clear();
                      bb.putDouble(11.21378923d);
                      deserializeDouble(arr, 0);
                      Unsafes.fastGetDouble(arr, 0);
                      // Unsafes.Really.fastGetDouble(arr, 0);
                      bb.flip();
                      bb.getDouble();
                  }
                  Thread.sleep(100);
              }

          }

          @Test
          public void performanceLong() throws Exception {
              byte[] arr = new byte[24];
              ByteBuffer bb = ByteBuffer.wrap(arr);

              for (int off = 0; off < 16; off++) {

                  long t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      serialize(0x1234567812345678L, arr, off);
                  long tSetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastSet(0x1234567812345678L, arr, off);
                  long tSetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastSet(0x1234567812345678L, arr, off);
                  // long tSetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++) {
                      bb.limit(off + 8);
                      bb.putLong(off, 0x1234567812345678L);
                  }
                  long tSetBb = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      deserializeLong(arr, off);
                  long tGetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastGetLong(arr, off);
                  long tGetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastGetLong(arr, off);
                  // long tGetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++) {
                      bb.position(off);
                      bb.getLong();
                  }
                  long tGetBb = System.nanoTime() - t0;

                  System.out.println( " long , offset= " + off + " : " );
                  System.out.println( " set/Java: " + nanosToTime(tSetJava));
                  System.out.println( " set/Unsafes: " + nanosToTime(tSetUnsafes));
                  // System.out.println( " set/Unsafes.Really: " + nanosToTime(tSetUnsafesReally));
                  System.out.println( " set/ByteBuffer: " + nanosToTime(tSetBb));
                  System.out.println( " get/Java: " + nanosToTime(tGetJava));
                  System.out.println( " get/Unsafes: " + nanosToTime(tGetUnsafes));
                  // System.out.println( " get/Unsafes.Really: " + nanosToTime(tGetUnsafesReally));
                  System.out.println( " get/ByteBuffer: " + nanosToTime(tGetBb));
              }

          }

          @Test
          public void performanceInt() throws Exception {
              byte[] arr = new byte[24];
              ByteBuffer bb = ByteBuffer.wrap(arr);

              for (int off = 0; off < 16; off++) {

                  long t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      serialize(0x12345678, arr, off);
                  long tSetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastSet(0x12345678, arr, off);
                  long tSetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastSet(0x12345678, arr, off);
                  // long tSetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++) {
                      bb.limit(off + 8);
                      bb.putInt(off, 0x12345678);
                  }
                  long tSetBb = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      deserializeInt(arr, off);
                  long tGetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastGetInt(arr, off);
                  long tGetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastGetInt(arr, off);
                  // long tGetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++) {
                      bb.position(off);
                      bb.getInt();
                  }
                  long tGetBb = System.nanoTime() - t0;

                  System.out.println( " int , offset= " + off + " : " );
                  System.out.println( " set/Java: " + nanosToTime(tSetJava));
                  System.out.println( " set/Unsafes: " + nanosToTime(tSetUnsafes));
                  // System.out.println( " set/Unsafes.Really: " + nanosToTime(tSetUnsafesReally));
                  System.out.println( " set/ByteBuffer: " + nanosToTime(tSetBb));
                  System.out.println( " get/Java: " + nanosToTime(tGetJava));
                  System.out.println( " get/Unsafes: " + nanosToTime(tGetUnsafes));
                  // System.out.println( " get/Unsafes.Really: " + nanosToTime(tGetUnsafesReally));
                  System.out.println( " get/ByteBuffer: " + nanosToTime(tGetBb));
              }

          }

          @Test
          public void performanceShort() throws Exception {
              byte[] arr = new byte[24];
              ByteBuffer bb = ByteBuffer.wrap(arr);

              for (int off = 0; off < 16; off++) {

                  long t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      serialize((short) 0x1234, arr, off);
                  long tSetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastSet((short) 0x1234, arr, off);
                  long tSetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastSet((short)0x1234, arr, off);
                  // long tSetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++) {
                      bb.limit(off + 8);
                      bb.putShort(off, (short)0x1234);
                  }
                  long tSetBb = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      deserializeShort(arr, off);
                  long tGetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastGetShort(arr, off);
                  long tGetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastGetShort(arr, off);
                  // long tGetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++) {
                      bb.position(off);
                      bb.getShort();
                  }
                  long tGetBb = System.nanoTime() - t0;

                  System.out.println( " short , offset= " + off + " : " );
                  System.out.println( " set/Java: " + nanosToTime(tSetJava));
                  System.out.println( " set/Unsafes: " + nanosToTime(tSetUnsafes));
                  // System.out.println( " set/Unsafes.Really: " + nanosToTime(tSetUnsafesReally));
                  System.out.println( " set/ByteBuffer: " + nanosToTime(tSetBb));
                  System.out.println( " get/Java: " + nanosToTime(tGetJava));
                  System.out.println( " get/Unsafes: " + nanosToTime(tGetUnsafes));
                  // System.out.println( " get/Unsafes.Really: " + nanosToTime(tGetUnsafesReally));
                  System.out.println( " get/ByteBuffer: " + nanosToTime(tGetBb));
              }

          }

          @Test
          public void performanceDouble() throws Exception {
              byte[] arr = new byte[24];
              ByteBuffer bb = ByteBuffer.wrap(arr);

              for (int off = 0; off < 16; off++) {

                  long t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      serialize(43.584d, arr, off);
                  long tSetJava = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n < LOOPS; n++)
                      Unsafes.fastSet(43.584d, arr, off);
                  long tSetUnsafes = System.nanoTime() - t0;

                  // t0 = System.nanoTime();
                  // for (int n = 0; n < LOOPS; n++)
                  // Unsafes.Really.fastSet(43.584d, arr, off);
                  // long tSetUnsafesReally = System.nanoTime() - t0;

                  t0 = System.nanoTime();
                  for (int n = 0; n


      ( This report has more than 16,000 characters and has been truncated. )

            shade Aleksey Shipilev
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: