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

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon May 25 09:58:52 UTC 2020


I've just integrated this - thanks to all the contributors and reviewers 
for the help!

Cheers
Maurizio

On 20/05/2020 15:01, Maurizio Cimadamore wrote:
> Another very small iteration which fixes a gap in the javadoc 
> specification for MemorySegment::toArray (thanks Chris!)
>
> Webrev:
>
> http://cr.openjdk.java.net/~mcimadamore/8243491_v5/webrev
>
> Delta webrev:
>
> http://cr.openjdk.java.net/~mcimadamore/8243491_v5/webrev_delta/
>
>
> Cheers
> Maurizio
>
> On 13/05/2020 13:12, Maurizio Cimadamore wrote:
>> Another iteration which addresses the latest CSR comments (the CSR 
>> has now been approved):
>>
>> * make MemorySegment::withAccessModes/hasAccessMode throw 
>> IllegalArgumentException in cases where the provided mask is invalid 
>> (this required a test tweak)
>> * sprinkled a couple of references to the JLS in the package javadoc, 
>> as per CSR suggestions
>> * Fixed the ParallelSum::findAny_bulk benchmarks, which were 
>> (erroneously) not testing all the elements in the segment
>>
>> Webrev:
>>
>> http://cr.openjdk.java.net/~mcimadamore/8243491_v4/webrev
>>
>> Delta webrev:
>>
>> http://cr.openjdk.java.net/~mcimadamore/8243491_v4/webrev_delta/
>>
>>
>> Cheers
>> Maurizio
>>
>> On 01/05/2020 12:06, Maurizio Cimadamore wrote:
>>> Latest iteration - the follow issues were addressed:
>>>
>>> * fix a bug with alignment of native segments triggering spurious 
>>> failures (contributed by Jorn)
>>> * fix javadoc and tests for access modes (contributed by Chris)
>>> * added benchmarks for Stream::findAny using segment spliterator 
>>> (contributed by Peter)
>>> * addressed CSR comments
>>>
>>> Webrev:
>>>
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v3/webrev
>>>
>>> Delta webrev:
>>>
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v3/webrev_delta/
>>>
>>> Javadoc:
>>>
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v3/javadoc
>>>
>>> Specdiff:
>>>
>>> http://cr.openjdk.java.net/~mcimadamore/8243491_v3/specdiff/overview-summary.html 
>>>
>>>
>>>
>>> Cheers
>>> Maurizio
>>>
>>> On 27/04/2020 13:13, Maurizio Cimadamore wrote:
>>>> 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