JDK-5095459 : (bf) Improve performance of ByteBuffer.putLong()
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2004-09-01
  • Updated: 2016-03-03
  • Resolved: 2016-03-03
Related Reports
Duplicate :  
Description
Name: jl125535			Date: 09/01/2004


A DESCRIPTION OF THE REQUEST :
Modify the HOTSPOT compiler to recognize ByteBuffer.putLong and generate optimized code that is equivalent to a series of hand coded assignments with shifts.

ByteBuffer.putLong executes 25% to 30% slower than corresponding shift operations:

long now = System.currentTimeMillis();
ByteBuffer bb = ByteBuffer.allocate(8);
bb.putLong(0, now); // is about 30% slower than the following

byte[] buf = new byte[8];
long now = System.currentTimeMillis();
buf[0] = (byte) (now >>> 56);
buf[1] = (byte) (now >>> 48);
// and so on


JUSTIFICATION :
Using ByteBuffer.putLong allows for much cleaner source code.  Modifying HOTSPOT to recognize ByteBuffer.putLong and generate optimized code will allow programmers to produce cleaner source code.

NOTE -- The test case focuses on ByteBuffer.putLong, but the same optimizations could be applied to putInt, and possibly others.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would like ByteBuffer.putLong() to execute as fast as a series of shifts.
ACTUAL -
ByteBuffer.putLong() runs 25% to 30% slower than a series of assignments with shifts.

.Time: 3.484 (testPutLongUsingShifts)
.Time: 4.469 (testPutLongUsingByteBuffer_wrap)
.Time: 4.422 (testPutLongUsingByteBuffer_allocate1a)
.Time: 4.671 (testPutLongUsingByteBuffer_allocate1b)
.Time: 4.766 (testPutLongUsingByteBuffer_allocateDirect)
.Time: 9.516 (testPutLongUsingByteBuffer_allocate2a)
.Time: 9.578 (testPutLongUsingByteBuffer_allocate2b)
.Time: 9.515 (testPutLongUsingByteBuffer_wrap2)

Time: 50.437

OK (8 tests)

---------- BEGIN SOURCE ----------
import junit.framework.TestCase;
import java.nio.ByteBuffer;

public class ByteBufferPerformanceTest extends TestCase {

  public static void main(String[] args) {
    junit.textui.TestRunner.run(ByteBufferPerformanceTest.class);
  }
  
  static final int times = 32000000;
  
  long begin = 0L;  // initialized in setUp()
  long end = 0;     // initialized in tearDown()

  protected void setUp() throws Exception {
    super.setUp();
    begin = System.currentTimeMillis();
  }

  protected void tearDown() throws Exception {
    super.tearDown();
    end = System.currentTimeMillis();
    double elapsed = (double)(end - begin) / 1000.;
    System.out.println("Time: " + elapsed + " (" + getName() + ")");
  }

  public void testPutLongUsingShifts()
  {
    byte[] buffer = new byte[16];

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      buffer[0] = (byte) (now >>> 56);
      buffer[1] = (byte) (now >>> 48);
      buffer[2] = (byte) (now >>> 40);
      buffer[3] = (byte) (now >>> 32);
      buffer[4] = (byte) (now >>> 24);
      buffer[5] = (byte) (now >>> 16);
      buffer[6] = (byte) (now >>> 8);
      buffer[7] = (byte) (now);
    }
  }

  public void testPutLongUsingByteBuffer_wrap()
  {
    byte[] buffer = new byte[16];
    ByteBuffer bb = ByteBuffer.wrap(buffer);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }

  public void testPutLongUsingByteBuffer_allocate1a()
  {
    ByteBuffer bb = ByteBuffer.allocate(8);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }

  public void testPutLongUsingByteBuffer_allocate1b()
  {
    ByteBuffer bb = ByteBuffer.allocate(8);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }

  public void testPutLongUsingByteBuffer_allocateDirect()
  {
    ByteBuffer bb = ByteBuffer.allocateDirect(8);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }

  public void testPutLongUsingByteBuffer_allocate2a()
  {
    ByteBuffer bb = ByteBuffer.allocate(8);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }

  public void testPutLongUsingByteBuffer_allocate2b()
  {
    ByteBuffer bb = ByteBuffer.allocate(8);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }

  public void testPutLongUsingByteBuffer_wrap2()
  {
    byte[] buffer = new byte[16];
    ByteBuffer bb = ByteBuffer.wrap(buffer);

    for(int i=0; i<times; ++i)
    {
      long now = System.currentTimeMillis();
      bb.putLong(0,now);
    }
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use a series of assignments with shifts as in  testPutLongUsingShifts test case.
(Incident Review ID: 301643) 
======================================================================

Comments
EVALUATION Need to check whether this is a hotspot warmup non-bug. If there is some performance to be won here, I'm not sure whether it belongs to the VM folks. ###@###.### 2004-09-24
24-09-2004