Ability to extend a MemorySegment

Michael Zucchi notzed at gmail.com
Wed Jan 22 03:37:45 UTC 2020


Ok fair enough, this wasn't clear at all; as i said there's probably a 
reason.  I don't think it's a good one but that's just one opinion.

This is what the top of the project page says:
--


  Project Panama: Interconnecting JVM and native code

We are improving and enriching the connections between the Java ^TM 
virtual machine and well-defined but “foreign” (non-Java) APIs, 
including many interfaces commonly used by C programmers.

To this end, Project Panama will include most or all of these components:

  * native function calling from JVM (C, C++), specifically per JEP 191
  * native data access from JVM or inside JVM heap
  * new data layouts in JVM heap
  * native metadata definition for JVM
  * header file API extraction tools (see below)
  * native library management APIs
  * native-oriented interpreter and runtime “hooks”
  * class and method resolution “hooks”
  * native-oriented JIT optimizations
  * tooling or wrapper interposition for safety
  * exploratory work with difficult-to-integrate native libraries

--

I think that's a big enough task already without complicating it for 
auxiliary purposes, and the design is more complex because of it.


On 21/1/20 10:28 pm, Maurizio Cimadamore wrote:
>
> On 21/01/2020 11:22, Michael Zucchi wrote:
>> I think MemorySegment is way over-engineered already, adding more 
>> complexity doesn't sound great.  I wish it was just a 1:1 mapping to 
>> a malloc block (or slice thereof), and had no support for arrays or 
>> array bytebuffers. 
>
> Well, you come from the perspective of someone who needs ABI support - 
> fine. But are you saying that, because MemorySegment have to play nice 
> with ABI, it is not important to support other related use cases which 
> have _nothing to do with ABI_ ?
>
> One cool thing you can do with segments is:
>
> * create a memory segment
> * map it into a ByteBuffer
> * use the ByteBuffer as usual
> * close the segment
>

I mean sure.  That's quite a related use for direct buffers and doesn't 
add anything weird to the api, they both use malloc to allocate the 
block and they can both be accessed from native calls.

Its the ability to represent non-malloc-memory as memory that's a bit weird.

> This gives you same old good ByteBuffer API (no var handles!) but with 
> deterministic deallocation on top (which is something that Java users 
> have been asking for ages, but that, given restrictions of the BB API 
> we couldn't fully deliver).
>
> Another thing segments allow you to do is to take a big file (over 2G) 
> and map slices of it to different BB, effectively allowing you to use 
> the BB API on stuff that is bigger than 2GB.
>
Well files are covered fine by FileChannel.map().  Large memory arrays 
aren't though.

> What you call over-engineering to me sounds more like you are probably 
> just not interested in the other half of use cases that this API is 
> about. With this I'm not saying the API as is is perfect, but in some 
> of the feedback we have received so far the recurrent theme seems to 
> be "I don't need XYZ, so it is just silly to have it in the API", or 
> "I really badly need XYZ so why didn't you just add it to the API" - 
> and all I'm saying here is that there are principles upon which some 
> of these decisions have been made.
>
Yes of course, I don't think you guys are just doing it to be difficult 
or making stuff up out of thin air.  As I said the thinking isn't 
obvious not the least from the project description. And I know there are 
always trade-offs with any decision.

Not that anybody would read it, maybe the project needs a FAQ that 
covers these issues.  And well if they're constantly being brought up 
whilst asking for feedback, maybe there's a reason.  I know it doesn't 
always appear that way but the criticism (at least mine) is intended to 
be constructive or to learn enough to be constructive. I've worked on 
public projects and the experience was miserable enough to break me so i 
don't envy you in the least.

>> And also that MemoryAddress was literally just an address inside of 
>> it's segment - and not an 'offset' which may or may not actually be 
>> an address (with no direct way to find out - from java). 
> If you really want to get a low-level address from a MemoryAddress you 
> can call ForeignUnsafe::getUnsafeOffset(MemoryAddress) and get the 
> 'long' address you need.

Ok thanks.  Not an obvious place to look and i presume that also works 
MemorySegment.allocateNative().  Generally they behave differently and 
look differently and it makes debugging quite painful.  I was trying to 
use offset() to determine if an address was actually null, but that did 
funny things with upcall handles until the last change to those which 
changed them to anonymous pointers.  That's just a weird gotcha.

>> The need to support this other stuff just makes the api weird and 
>> confusing since the naming conventions don't make sense if it was 
>> just backed by malloc and for all that you can never actually use 
>> these array-based segments with "foreign" functions anyway.
> See above. There's more to memory segments than just ABI.
>> It this stuff there just as a (clumsy) mechanism to allow for the 
>> general copy() method?
> Please expand a bit more - are you referring to MemoryAddress::copy? 
> Why is it clumsy (same interface as System.arrayCopy which has served 
> us well for quite a bit).
Well It's clumsy in that memorysegment first needs to support all these 
different backend types (clumsy required design), and then you need to 
create a memorysegment wrapping one to use it (clumsy to use).

A copy-to-array, and copy-from-array (or to/from native) pair would also 
provide such functionality and be more like system.arraycopy (including 
having to be native/internal and not implemented in java).  Or one could 
just bounce it through a *Buffer view - which is also a bit clumsy 
(particularly with the default-network-byte-order) but it is what it is.

>> I'm sure there are reasons it's the way it is but the whole thing is 
>> just so, well, 'foreign', to anything from either C or Java.
>
>> And an even more restricted allocator? Ouch. 
>
> Again, you are jumping ahead and misreading what I said. What I was 
> trying to explain is that there are a number of considerations which 
> might affect the decision of whether something like 'realloc' really 
> belongs to the MemorySegment API such as:
>
> * which allocator segments ended up being based upon
> * the fact that, like it or not, not all segments will be able to 
> support this operation anyway
> * whether it's important enough to deserve a place in the API anyway 
> (seems like, e.g. you and Ty disagree on how much important realloc 
> actually is)
>
> Sure, we can pretend all these choices do not exist - but in reality 
> they do. And again, are you saying "ouch" to having an allocator which 
> will give you faster performances and better scalability than malloc, 
> and better integration with the VM ecosystem as a whole? On what 
> basis? Do you know that real-world projects like e.g. Netty [1] are 
> building their own allocators anyway as they find malloc/free (and, by 
> consequence, ByteBufer) to be hugely unfeasible for them?
>

If malloc is too slow you allocate a bigger segment and dole out 
addresses from it, the same solution one uses in C.  Having a stack 
allocator as an option makes some sense - this is both a very common use 
when calling native functions, and seems to be the main "intended" 
use-case of allocateNative() - but such an allocator can be trivally 
implemented atop MemorySegment over malloc.  But having it as the only 
type is just going to be too restrictive - it certainly wouldn't work 
for twitter's case.

Things like netty just demonstrate you can't handle every case 
automatically and applications are free to handle pathalogical cases 
with novel data structures.  This will always be true.  The issues with 
direct bytebuffer are well known for some applications and the blog post 
just demonstrates they can be mitigated even within the current jvm.  
 From JNI it's trivial to wrap a malloc() memory in a bytebuffer, it's 
just weird that's the only way presently.  And their example shows it 
isn't the native allocation that's the main issue anyway as it happens 
with java memory too.

One of the (many) cargo cults for java for years has been to avoid 
old-generation at all costs, but that post just demonstrates you can't 
rely on the gc in all cases.  Lost of "modern java" like Streams 
processing also encourages or requires practices that generate more garbage.




More information about the panama-dev mailing list