RFR 8151163 All Buffer implementations should leverage Unsafe unaligned accessors

Paul Sandoz paul.sandoz at oracle.com
Tue Mar 8 18:27:33 UTC 2016


> On 8 Mar 2016, at 14:37, Aleksey Shipilev <aleksey.shipilev at oracle.com> wrote:
> 
> On 03/08/2016 04:06 PM, Paul Sandoz wrote:
>> Please pre-emptively review a fix to update the buffer implementations to leverage the Unsafe unaligned accessors:
>> 
>>  http://cr.openjdk.java.net/~psandoz/jdk9/JDK-8151163-buffer-unsafe-unaligned-access/webrev/
> 
> *) My concern with using double-register Unsafe calls is that compilers
> are unable to speculate on (hb == null) value, which means you will have
> the additional field read on the fast path. See:
>   https://bugs.openjdk.java.net/browse/JDK-8150921
>   http://cr.openjdk.java.net/~shade/8150921/notes.txt
> 
> So, while I agree that using double-register unaligned accessors are
> cleaner, I'd try to special-case (bb.hb == null) case for Heap* buffers.
> Current patch might still be better than going through Bits though.
> 

The fix is conservative. I did not change anything in direct buffer that currently uses the single addressing mode, that’s a much larger change of course, and as you point out requires deeper investigation.

To clarify there are two kinds of change:

1) The access methods of direct ByteBuffer, such as getInt etc now use the unaligned accessors.

Direct-X-Buffer-bin.java.template
—
     private $type$ get$Type$(long a) {
-        if (unaligned) {
-            $memtype$ x = unsafe.get$Memtype$(a);
-            return $fromBits$(nativeByteOrder ? x : Bits.swap(x));
-        }
-        return Bits.get$Type$(a, bigEndian);
+        $memtype$ x = unsafe.get$Memtype$Unaligned(null, a, bigEndian);
+        return $fromBits$(x);
     }

     public $type$ get$Type$() {
@@ -51,12 +48,8 @@

     private ByteBuffer put$Type$(long a, $type$ x) {
 #if[rw]
-        if (unaligned) {
-            $memtype$ y = $toBits$(x);
-            unsafe.put$Memtype$(a, (nativeByteOrder ? y : Bits.swap(y)));
-        } else {
-            Bits.put$Type$(a, x, bigEndian);
-        }
+        $memtype$ y = $toBits$(x);
+        unsafe.put$Memtype$Unaligned(null, a, y, bigEndian);
         return this;
 #else[rw]
         throw new ReadOnlyBufferException();


Note the use of null here, so we should fine for this case.


2) Changes to the view implementations used by heap buffer for aligned and misaligned access and direct byte buffer for misaligned access. This is where we replace the singular call to bits:

ByteBufferAs-X-Buffer.java.template
—
     public $type$ get() {
-        return Bits.get$Type$$BO$(bb, ix(nextGetIndex()));
+        $memtype$ x = unsafe.get$Memtype$Unaligned(bb.hb, bb.address + ix(nextGetIndex()),
+            {#if[boB]?true:false});
+        return $fromBits$(x);
     }

     public $type$ get(int i) {
-        return Bits.get$Type$$BO$(bb, ix(checkIndex(i)));
+        $memtype$ x = unsafe.get$Memtype$Unaligned(bb.hb, bb.address + ix(checkIndex(i)),
+            {#if[boB]?true:false});
+        return $fromBits$(x);
     }

This should be an improvement when used by heap ByteBuffer implementations, much the same way it was when previously changing the heap ByteBuffer accessors to use Unsafe unaligned access.

So that leaves the direct ByteBuffer case:

DirectByteBuffer
—
public IntBuffer asIntBuffer() {
    int off = this.position();
    int lim = this.limit();
    assert (off <= lim);
    int rem = (off <= lim ? lim - off : 0);

    int size = rem >> 2;
    if (!unaligned && ((address + off) % (1 << 2) != 0)) {
        return (bigEndian
                ? (IntBuffer)(new ByteBufferAsIntBufferB(this,
                                                                   -1,
                                                                   0,
                                                                   size,
                                                                   size,
                                                                   off))
                : (IntBuffer)(new ByteBufferAsIntBufferL(this,
                                                                   -1,
                                                                   0,
                                                                   size,
                                                                   size,
                                                                   off)));


Specially the same implementations used by heap ByteBuffers are used here for misaligned view access. In this case it’s Bits (with N ByteBuffer.get/put calls + composition to/from the wider unit) vs. Unsafe unaligned access + field read of hb (which is null), where the latter Unsafe access might be an intrinsic. On x86 that should be a win.

One goal here is to consistently use the Unsafe unaligned access to leverage any intrinsification now, or in the future, on other platforms e.g. SPARC.


>> The changes in this webrev take advantage of those for JDK-8149469
>> and apply the unsafe double addressing scheme so certain byte buffer
>> view implementations can work across heap and direct buffers. This
>> should improve the performance on x86 for:
> 
> I understand the idea, but I think we would need to verify this before
> pushing.
> 

Admittedly i am leaning on the rational/motivation for the previous changes to use Unsafe unaligned accessors.

I less confident about the impact on non-x86 platforms.

I have some VarHandles related benchmark code [*] i can use to get some numbers.

Paul.

[*] But getting side-tracked with a VH perf regressions i observed when running the test, something has changed underneath me in hs-comp that is causing the linkage to not wire up optimally :-(



More information about the core-libs-dev mailing list