RFR 8243491: Implementation of Foreign-Memory Access API (Second Incubator)

forax at univ-mlv.fr forax at univ-mlv.fr
Tue Apr 28 07:58:17 UTC 2020


> De: "mandy chung" <mandy.chung at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>, "Maurizio Cimadamore"
> <maurizio.cimadamore at oracle.com>
> Cc: "core-libs-dev" <core-libs-dev at openjdk.java.net>
> Envoyé: Mardi 28 Avril 2020 01:17:05
> Objet: Re: RFR 8243491: Implementation of Foreign-Memory Access API (Second
> Incubator)

> On 4/27/20 2:42 PM, Remi Forax wrote:

>> Hi Maurizio, Mandy,

>> In [
>> https://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java.udiff.html
>> |
>> https://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java.udiff.html
>> ] ,
>> using a condy inside a static init make me sad,
>> using a late loading constant to early load it in the static init seems counter
>> natural.

> This is an interim solution due to JDK-8243492 which causes VM crash if it
> lazily loads the constant.
interesting, it's because the BSM is an instance method and not a static method 

>> I don't think you need to store all the values into static fields, you can
>> directly do a ldc + aaload with the right index right where you need it,

> I think this is what you are thinking as reported in JDK-8243492:
> [ http://cr.openjdk.java.net/~mchung/jdk15/webrevs/8239578/webrev.01-accessor/ |
> http://cr.openjdk.java.net/~mchung/jdk15/webrevs/8239578/webrev.01-accessor/ ]

if the accessors are declared ACC_STATIC, yes ! 

> Mandy
Rémi 

>> ASM will generates only one condy constant pool constant for all ldcs (so once
>> one of the ldc is executed the classdata will be available to the other ldcs)
>> and the JIT will fold the access to the array given the array is a constant.

>> regards,
>> Rémi

>> ----- Mail original -----

>>> De: "Maurizio Cimadamore" [ mailto:maurizio.cimadamore at oracle.com |
>>> <maurizio.cimadamore at oracle.com> ] À: "core-libs-dev" [
>>> mailto:core-libs-dev at openjdk.java.net | <core-libs-dev at openjdk.java.net> ]
>>> Envoyé: Lundi 27 Avril 2020 14:13:55
>>> Objet: Re: RFR 8243491: Implementation of Foreign-Memory Access API (Second
>>> Incubator)

>>> Another iteration, which addresses the following issues:

>>> * wrong copyright headers in certain tests
>>> * issue with TestNative when running in debug mode caused by mismatched
>>> malloc/os::free (contributed by Jorn)
>>> * clarify javadoc of MemoryHandles::withStride
>>> * improved implementation of MemoryAccessVarHandleGenerator to use
>>> hidden classes rather than Unsafe.dAC (contributed by Mandy)

>>> Webrev: [ http://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev |
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev ] Delta webrev: [
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev_delta/ |
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev_delta/ ] Javadoc: [
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/javadoc |
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/javadoc ] Specdiff: [
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/specdiff/overview-summary.html
>>> |
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/specdiff/overview-summary.html
>>> ] Cheers
>>> Maurizio

>>> On 23/04/2020 21:33, Maurizio Cimadamore wrote:

>>>> Hi,
>>>> time has come for another round of foreign memory access API
>>>> incubation (see JEP 383 [3]). This iteration aims at polishing some of
>>>> the rough edges of the API, and adds some of the functionalities that
>>>> developers have been asking for during this first round of incubation.
>>>> The revised API tightens the thread-confinement constraints (by
>>>> removing the MemorySegment::acquire method) and instead provides more
>>>> targeted support for parallel computation via a segment spliterator.
>>>> The API also adds a way to create a custom native segment; this is,
>>>> essentially, an unsafe API point, very similar in spirit to the JNI
>>>> NewDirectByteBuffer functionality [1]. By using this bit of API,
>>>> power-users will be able to add support, via MemorySegment, to *their
>>>> own memory sources* (e.g. think of a custom allocator written in
>>>> C/C++). For now, this API point is called off as "restricted" and a
>>>> special read-only JDK property will have to be set on the command line
>>>> for calls to this method to succeed. We are aware there's no precedent
>>>> for something like this in the Java SE API - but if Project Panama is
>>>> to remain true about its ultimate goal of replacing bits of JNI code
>>>> with (low level) Java code, stuff like this has to be *possible*. We
>>>> anticipate that, at some point, this property will become a true
>>>> launcher flag, and that the foreign restricted machinery will be
>>>> integrated more neatly into the module system.

>>>> A list of the API, implementation and test changes is provided below.
>>>> If you have any questions, or need more detailed explanations, I (and
>>>> the rest of the Panama team) will be happy to point at existing
>>>> discussions, and/or to provide the feedback required.

>>>> Thanks
>>>> Maurizio

>>>> Webrev: [ http://cr.openjdk.java.net/~mcimadamore/8243491_v1/webrev |
>>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/webrev ] Javadoc: [
>>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/javadoc |
>>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/javadoc ] Specdiff: [
>>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/specdiff/overview-summary.html
>>>> |
>>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/specdiff/overview-summary.html
>>>> ] CSR: [ https://bugs.openjdk.java.net/browse/JDK-8243496 |
>>>> https://bugs.openjdk.java.net/browse/JDK-8243496 ] API changes
>>>> ===========

>>>> * MemorySegment
>>>>   - drop support for acquire() method - in its place now you can
>>>> obtain a spliterator from a segment, which supports divide-and-conquer
>>>>   - revamped support for views - e.g. isReadOnly - now segments have
>>>> access modes
>>>>   - added API to do serial confinement hand-off
>>>> (MemorySegment::withOwnerThread)
>>>>   - added unsafe factory to construct a native segment out of an
>>>> existing address; this API is "restricted" and only available if the
>>>> program is executed using the -Dforeign.unsafe=permit flag.
>>>>   - the MemorySegment::mapFromPath now returns a MappedMemorySegment
>>>> * MappedMemorySegment
>>>>   - small sub-interface which provides extra capabilities for mapped
>>>> segments (load(), unload() and force())
>>>> * MemoryAddress
>>>>   - added distinction between *checked* and *unchecked* addresses;
>>>> *unchecked* addresses do not have a segment, so they cannot be
>>>> dereferenced
>>>>   - added NULL memory address (it's an unchecked address)
>>>>   - added factory to construct MemoryAddress from long value (result
>>>> is also an unchecked address)
>>>>   - added API point to get raw address value (where possible - e.g. if
>>>> this is not an address pointing to a heap segment)
>>>> * MemoryLayout
>>>>   - Added support for layout "attributes" - e.g. store metadata inside
>>>> MemoryLayouts
>>>>   - Added MemoryLayout::isPadding predicate
>>>>   - Added helper function to SequenceLayout to rehape/flatten sequence
>>>> layouts (a la NDArray [4])
>>>> * MemoryHandles
>>>>   - add support for general VarHandle combinators (similar to MH
>>>> combinators)
>>>>   - add a combinator to turn a long-VH into a MemoryAddress VH (the
>>>> resulting MemoryAddress is also *unchecked* and cannot be dereferenced)

>>>> Implementation changes
>>>> ======================

>>>> * add support for VarHandle combinators (e.g. IndirectVH)

>>>> The idea here is simple: a VarHandle can almost be thought of as a set
>>>> of method handles (one for each access mode supported by the var
>>>> handle) that are lazily linked. This gives us a relatively simple idea
>>>> upon which to build support for custom var handle adapters: we could
>>>> create a VarHandle by passing an existing var handle and also specify
>>>> the set of adaptations that should be applied to the method handle for
>>>> a given access mode in the original var handle. The result is a new
>>>> VarHandle which might support a different carrier type and more, or
>>>> less coordinate types. Adding this support was relatively easy - and
>>>> it only required one low-level surgery of the lambda forms generated
>>>> for adapted var handle (this is required so that the "right" var
>>>> handle receiver can be used for dispatching the access mode call).

>>>> All the new adapters in the MemoryHandles API (which are really
>>>> defined inside VarHandles) are really just a bunch of MH adapters that
>>>> are stitched together into a brand new VH. The only caveat is that, we
>>>> could have a checked exception mismatch: the VarHandle API methods are
>>>> specified not to throw any checked exception, whereas method handles
>>>> can throw any throwable. This means that, potentially, calling get()
>>>> on an adapted VarHandle could result in a checked exception being
>>>> thrown; to solve this gnarly issue, we decided to scan all the filter
>>>> functions passed to the VH combinators and look for direct method
>>>> handles which throw checked exceptions. If such MHs are found (these
>>>> can be deeply nested, since the MHs can be adapted on their own),
>>>> adaptation of the target VH fails fast.

>>>> * More ByteBuffer implementation changes

>>>> Some more changes to ByteBuffer support were necessary here. First, we
>>>> have added support for retrieval of "mapped" properties associated
>>>> with a ByteBuffer (e.g. the file descriptor, etc.). This is crucial if
>>>> we want to be able to turn an existing byte buffer into the "right
>>>> kind" of memory segment.

>>>> Conversely, we also have to allow creation of mapped byte buffers
>>>> given existing parameters - which is needed when going from (mapped)
>>>> segment to a buffer. These two pieces together allow us to go from
>>>> segment to buffer and back w/o losing any information about the
>>>> underlying memory mapping (which was an issue in the previous
>>>> implementation).

>>>> Lastly, to support the new MappedMemorySegment abstraction, all the
>>>> memory mapped supporting functionalities have been moved into a common
>>>> helper class so that MappedMemorySegmentImpl can reuse that (e.g. for
>>>> MappedMemorySegment::force).

>>>> * Rewritten memory segment hierarchy

>>>> The old implementation had a monomorphic memory segment class. In this
>>>> round we aimed at splitting the various implementation classes so that
>>>> we have a class for heap segments (HeapMemorySegmentImpl), one for
>>>> native segments (NativeMemorySegmentImpl) and one for memory mapped
>>>> segments (MappedMemorySegmentImpl, which extends from
>>>> NativeMemorySegmentImpl). Not much to see here - although one
>>>> important point is that, by doing this, we have been able to speed up
>>>> performances quite a bit, since now e.g. native/mapped segments are
>>>> _guaranteed_ to have a null "base". We have also done few tricks to
>>>> make sure that the "base" accessor for heap segment is sharply typed
>>>> and also NPE checked, which allows C2 to speculate more and hoist.
>>>> With these changes _all_ segment types have comparable performances
>>>> and hoisting guarantees (unlike in the old implementation).

>>>> * Add workarounds in MemoryAddressProxy, AbstractMemorySegmentImpl to
>>>> special case "small segments" so that VM can apply bound check
>>>> elimination

>>>> This is another important piece which allows to get very good
>>>> performances out of indexes memory access var handles; as you might
>>>> know, the JIT compiler has troubles in optimizing loops where the loop
>>>> variable is a long [2]. To make up for that, in this round we add an
>>>> optimization which allows the API to detect whether a segment is
>>>> *small* or *large*. For small segments, the API realizes that there's
>>>> no need to perform long computation (e.g. to perform bound checks, or
>>>> offset additions), so it falls back to integer logic, which in turns
>>>> allows bound check elimination.

>>>> * renaming of the various var handle classes to conform to "memory
>>>> access var handle" terminology

>>>> This is mostly stylistic, nothing to see here.

>>>> Tests changes
>>>> =============

>>>> In addition to the tests for the new API changes, we've also added
>>>> some stress tests for var handle combinators - e.g. there's a flag
>>>> that can be enabled which turns on some "dummy" var handle adaptations
>>>> on all var handles created by the runtime. We've used this flag on
>>>> existing tests to make sure that things work as expected.

>>>> To sanity test the new memory segment spliterator, we have wired the
>>>> new segment spliterator with the existing spliterator test harness.

>>>> We have also added several micro benchmarks for the memory segment API
>>>> (and made some changes to the build script so that native libraries
>>>> would be handled correctly).

>>>> [1] - [
>>>> https://docs.oracle.com/en/java/javase/14/docs/specs/jni/functions.html#newdirectbytebuffer
>>>> |
>>>> https://docs.oracle.com/en/java/javase/14/docs/specs/jni/functions.html#newdirectbytebuffer
>>>> ] [2] - [ https://bugs.openjdk.java.net/browse/JDK-8223051 |
>>>> https://bugs.openjdk.java.net/browse/JDK-8223051 ] [3] - [
>>>> https://openjdk.java.net/jeps/383 | https://openjdk.java.net/jeps/383 ] [4] - [
>>>> https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html#numpy.reshape
>>>> |
>>>> https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html#numpy.reshape
>>>> ]


More information about the core-libs-dev mailing list