On 24/04/2020 17:38, Paul Sandoz wrote:

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.

That comment is bogus. In the previous iteration the stride was positive - now we removed all restrictions so that you can go forwards and backwards; we added zero for completeness but the javadoc does not reflect that (I mean, the @throws does, but not the main method description).

I'll rectify that

Thanks
Maurizio


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