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

Remi Forax forax at univ-mlv.fr
Mon Apr 27 21:42:16 UTC 2020


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,
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.

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,
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" <maurizio.cimadamore at oracle.com>
> À: "core-libs-dev" <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
> 
> Delta webrev:
> 
> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/webrev_delta/
> 
> Javadoc:
> 
> http://cr.openjdk.java.net/~mcimadamore/8243491_v2/javadoc
> 
> Specdiff:
> 
> 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
>>
>> Javadoc:
>>
>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/javadoc
>>
>> Specdiff:
>>
>> http://cr.openjdk.java.net/~mcimadamore/8243491_v1/specdiff/overview-summary.html
>>
>>
>> CSR:
>>
>> 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
>> [2] - https://bugs.openjdk.java.net/browse/JDK-8223051
>> [3] - https://openjdk.java.net/jeps/383
>> [4] -
>> https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html#numpy.reshape
>>


More information about the core-libs-dev mailing list