> On Apr 23, 2020, at 5:45 PM, Maurizio Cimadamore 
> <maurizio.cimadam...@oracle.com> wrote:
> 
> 
> On 24/04/2020 01:35, Paul Sandoz wrote:
>> Hi,
>> 
>> Looks good. I have seen almost all of this in reviews on panama-dev hence 
>> the lack of substantial comments here.
>> 
>> I suspect we are not gonna need the drop argument VH combinator, dropping 
>> coordinates feels a little suspicious to me, but I can see why it's there 
>> for completeness.
> 
> Thanks Paul.
> 
> Re. drop coordinates, note that we're actually using it in 
> MemoryHandles.withStride, so that we can insert a dummy coordinate that is 
> always discarded in case the stride is zero. We could do without it as well, 
> of course, but sometimes it's hard to have a sense of how these pieces might 
> be joined in practice.
> 

Ah, ok I can see its value now when dropping a coordinate that would otherwise 
result in some redundant calculation.  However, looking at this more closely:

* @param bytesStride the stride, in bytes, by which to multiply the coordinate 
value. Must be greater than zero.

It implies that a zero value should be disallowed contrary to the 
implementation.  I am wondering if your intent was support a signed stride 
value?

In this case it could be argued that a zero stride is misleading, if supported, 
since any value passed for the coordinate X has no effect.  But I can also see 
the other side from a position uniformity, and then why not support negative 
strides, which I think the implementation does support.

Paul.

> Maurizio
> 
>> Paul.
>> 
>>> On Apr 23, 2020, at 1:33 PM, Maurizio Cimadamore 
>>> <maurizio.cimadam...@oracle.com> 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
>>> 
>>> 
> 

Reply via email to