sun.nio.ch.DirectBuffer and jdk9/jigsaw
paul.sandoz at oracle.com
Thu Feb 23 20:52:54 UTC 2017
> On 23 Feb 2017, at 10:05, Remi Forax <forax at univ-mlv.fr> wrote:
> Hi Vitaly,
> I believe that what you are looking for is MethodHandles.byteBufferViewVarHandle  that creates a VarHandle that allows to do aligned/unaligned access on a ByteBuffer.
> It should be a little slower that using Unsafe because of the bound check (and the nullcheck but it is usually removed by the VM).
VarHandles for byte buffer views was primarily added to support enhanced atomic access to the contents of a byte buffer, all the methods route through to the internal Unsafe methods with safety checks (which for the most part can be optimized away, in loops at least, such as array bounds checks, read only checks, alignment checks for atomic ops).
As part of the general work to encourage people not to use Unsafe we also made improvements to ByteBuffer (heap and direct) view access in JDK 9, primarily for x86 platforms (or those that support misaligned loads/stores).
The performance analysis we did showed that VarHandle plain access and ByteBuffer view access are similar and C2 can generate good code in unrolled loops. So if you want plain access a ByteBuffer might be sufficient.
Of course this will not be sufficient for those really pushing the boundaries. (It’s a race where we often behind trying to catch up!)
The general way forward is Project Panama, where we can have better control over memory alloc/free, the overlay of structure, and safe pointers to such allocated structures. There will be optimisation challenges and value types are needed for a good API.
(I am unsure if it might be possible to do something lower-level in the interim with minimal value types.)
(I am wondering if Andrew Haley’s discussion on jmm-dev about safe deallocation of direct byte buffers also holds some clues for efficient safe pointer management.)
> That's said, unaligned access are faster with a VarHandle because sun.misc.Unsafe (which is stuck in the Java 8 world unlinke jdk.internal.misc.Unsafe) does not provide a way to do an unaligned access so you have do multiple read which is slower.
> ----- Mail original -----
>> De: "Vitaly Davidovich" <vitalyd at gmail.com>
>> À: "Uwe Schindler" <uschindler at apache.org>
>> Cc: "jigsaw-dev" <jigsaw-dev at openjdk.java.net>
>> Envoyé: Jeudi 23 Février 2017 18:05:36
>> Objet: Re: sun.nio.ch.DirectBuffer and jdk9/jigsaw
>> On Thu, Feb 23, 2017 at 11:54 AM, Uwe Schindler <uschindler at apache.org>
>>> Why do you need the address at all in the Java code? Java code can use the
>>> official ByteBuffer methods to access the memory you are wrapping. In Java
>>> 9 that’s optimized very good by Hotspot and should be almost as fast as
>>> array accesses (we proved that in Apache Lucene - congrats to the Hotspot
>>> committers). If you need special access modes like volatile access, then
>>> you can use Java 9's VarHandles. You can get a VarHandle to the backing
>>> direct buffer using the MethodHandles API.
>> I mentioned this upthread - the base address is used for index calculations
>> to read/write data using Unsafe directly. I don't know about Java 9 as
>> I've not tried it yet, but the generated assembly for using BB methods vs
>> Unsafe did not favor BB. There are also various safety checks in
>> DBB/Buffer internals that won't get optimized away.
>> Also, my general experience with looking at C2 optimizations has led me to
>> the conclusion that the optimizations are "unstable" - innocent code
>> changes, differences in order of how classes are loaded, differences in
>> what callsites trigger compilation first, and a bunch of other otherwise
>> benign things can interfere with inlining decisions, which is typically the
>> reason things go sideways in terms of optimization.
>> As for MethodHandles and VarHandles, that's one possibility I'm considering
>> as a way to migrate off using DirectBuffer (although I'd still like to know
>> if there's any plan to standardize/formalize some notion of a direct
>> buffer). However, my understanding is that using MH will still require me
>> to crack into jdk code (to get access to the DBB and friends) and thus
>> require addExports. DirectBuffer is still accessible if using addExports,
>> but it's a wrinkle I was hoping to iron out, hence what started this email
>>> Uwe Schindler
>>> uschindler at apache.org
>>> ASF Member, Apache Lucene PMC / Committer
>>> Bremen, Germany
>>>> -----Original Message-----
>>>> From: jigsaw-dev [mailto:jigsaw-dev-bounces at openjdk.java.net] On Behalf
>>>> Of Vitaly Davidovich
>>>> Sent: Thursday, February 23, 2017 5:30 PM
>>>> To: Chris Hegarty <chris.hegarty at oracle.com>
>>>> Cc: jigsaw-dev <jigsaw-dev at openjdk.java.net>
>>>> Subject: Re: sun.nio.ch.DirectBuffer and jdk9/jigsaw
>>>> On Thu, Feb 23, 2017 at 11:10 AM, Chris Hegarty
>>>> <chris.hegarty at oracle.com>
>>>>>> On 23 Feb 2017, at 11:30, Vitaly Davidovich <vitalyd at gmail.com>
>>>>>> The buffers are reused by having them point to different native
>>>>>> block addresses; those blocks are managed by native code. As
>>>>>> the ByteBuffer (DirectByteBuffer concretely) is used as the Java
>>>>>> interface/view of native memory, allowing Java and native code to
>>>>> So a DBB, under your code, may report a different address at some time
>>>>> in the future, to that of what it currently reports?
>>>>> I was not aware of this
>>>>> usecase. Is any similar code available on the web, or elsewhere, so we
>>>>> could try to determine why this is being done?
>>>> Unfortunately it's not open source code, and I don't immediately know of
>>>> anything similar on the web (or otherwise). However, the gist is the
>>>> 1) Allocate a 0-size DBB (i.e. ByteBuffer.allocateDirect(0)). This gives
>>>> you a Java "handle", if you will, to some native memory. But, since this
>>>> DBB will be attached/reattached to different memory dynamically, there's
>>>> need for an actual allocation.
>>>> 2) Native code wants to expose a segment of memory to Java. In JNI, it
>>>> sets the address and capacity of this DBB to the pointer where the native
>>>> memory segment starts, and to the capacity (it knows how big the native
>>>> segment is). Java code asks for this DBB to be "attached" to, say, some
>>>> sort of message, and the JNI/native code perform these functions.
>>>> 3) Java gets the attached DBB back, and can then use its API
>>>> (getXXX/setXXX) to read/write that native block. Once the operation
>>>> completes, the DBB is recycled for reuse (i.e. can be attached to a
>>>> different native segment again).
>>>> Obviously, we can use
>>>> to get the address and then expose that via a JNI helper - in fact,
>>>> what was done before. But, there's a JNI call penalty here for what is
>>>> otherwise a memory read. DirectBuffer::address() solves that nicely, and
>>>> also plays well with the C2 JIT (as mentioned) because the callsites
>>>> this is used only see DBB, and then the whole invokeinterface call is
>>>> devirtualized and inlined into a quick type check and Java field read -
>>>> performance of this is, as you can imagine, significantly better than the
>>>> JNI approach.
>>>> If you think of what a DBB really is, it's pretty much what it's name
>>>> suggests - it's an API to read/write to native memory, rather than Java
>>>> heap memory (i.e. HeapByteBuffer). But, there's no reason the native
>>>> memory backing the DBB has to also be allocated via Unsafe itself,
>>>> that's the more common scenario.
>>>> On the Java side, consumers of this have a common and conventional API
>>>> a byte buffer, i.e. ByteBuffer, which can optionally be used in the
>>>> above (obviously callers will need to know what mode they're using).
More information about the jigsaw-dev