<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p>Hi,<br>
Yes, this is the correcty way to provide feedback - thank you for
asking. This reminds me of the discussion on copyFrom from some
time ago. <br>
</p>
<p><a class="moz-txt-link-freetext" href="https://mail.openjdk.org/pipermail/panama-dev/2021-April/012872.html">https://mail.openjdk.org/pipermail/panama-dev/2021-April/012872.html</a></p>
<p>In that case, we felt that the burden of (a) creating an heap
view of an array, then (b) slice source/dest segment accordingly
and (c) translate array "logical" offsets into segment "physical"
offsets was just too painful, so we provided static alternatives.
In terms of performance it was not super easy to find cases where
the static method was better (e.g. microbenchmarks would often
reveal no difference). I believe most of the perceived "speedup"
is transient, as eventually the slices _will_ be scalarized; but
in the case of long running methods like bulk copy and mismatch,
it takes _longer_ for the code to become hot enough so that
scalarization would be applied, which created some tension. I
recall a discussion along these lines with Uwe, who was trying to
use memory segments in Lucene/Solr.<br>
</p>
<p>Back to your question, I sympathize with your use case: a static
MemorySegment.mismatch is no different than the static
MemorySegment::copy flavor we currently provide (the one that
accepts two segments). And, even for mismatch it might make sense
to add overloads for arrays in dest/src position (so now there's
three methods).<br>
</p>
<p>That said, looking more broadly at the API, there are several
instance methods which might be subject to the same treatment,
basically nearly every instance method, except for the dereference
methods (which already take an offset, more on that later) and
toString/equals/hashcode. Consider the following examples that you
might also have included in your email:</p>
<p>```<br>
val seg = mem.asSlice(offset, size);<br>
seg.fill((byte)42);<br>
seg.isLoaded();<br>
seg.force();<br>
seg.spliterator();<br>
seg.elements(JAVA_INT);<br>
seg.toArray(JAVA_INT);<br>
... // the list goes on<br>
```<br>
</p>
<p>I think that copy (and now mismatch) were "picked on" the most
because they are binary methods - they accept two segments. So the
amount of slicing is doubled there, compared to the ones I showed
above. But the issue remains: how friendly should the API be to
clients that don't want (or can't) use slicing, but want to stick
to one big segment and then passing an offset as a side-channel
instead? <br>
</p>
<p>DIfferent APIs deal with this problem in different ways. The
ByteBuffer API, somewhat surprisingly, provides no static methods
at all, although the bulk copy get/put do provide Java array
overloads, so that you don't have to wrap/slice unnecessarily.
Also, many methods in the Buffer API take an int offset which,
again, helps reducing the amount of slicing going on (although
some of these methods were added later[1]). Then there's arrays,
which have relatively little info (just a length), and where all
capabilities are exposed through a companion "side-car" class,
namely j.u.Arrays.</p>
<p>While I'm fine, in principle, with both approaches, I'm afraid to
steer the API into a middle ground where it's a bit like
ByteBuffer, a bit like Arrays. Or, if we go there, we should have
a clear/defensible basis on how the bucketization works (which I
realize we don't have right now). So, I encourage us not to have a
discussion about "mismatch", but, rather, to have a broader
discussion on how methods in the MemorySegment class should be
"bucketized" (or we will be doomed to have this very same
conversation again).<br>
</p>
<p>Some addional points/observations: first, in your example, you
seem to have a `mem` abstraction that stores a segment and then an
offset. In other words, it seems like you have implemented your
own slicing _on top_. In an alternate world where your API relied
on slicing, your snippet would become just:</p>
<p>```<br>
<span style="font-family:monospace">return mem.mismatch(other.mem)
== -1L<br>
```<br>
</span><span style="font-family:monospace"></span></p>
<p>As there would be no offset around (the offset would be embedded
into the segment). So, I feel that in order to answer some of the
questions re. bucketization, we (or at least I) need to understand
why memory segment slices are not good enough a carrier for use
cases like yours.</p>
<p>Secondly, before we get too static-happy, let's also remember
that sometimes dealing with instance methods is generally nice -
e.g. I think<br>
<br>
```<br>
segment.elements(JAVA_INT)<br>
```<br>
<br>
looks/reads better than:<br>
<br>
```<br>
MemorySegment.element(segment, JAVA_INT)<br>
```</p>
<p>And the more static calls you have, the more it shows. Ability of
using method chaining in these cases is nice.</p>
<p>Last, as hinted above, I note that our API has not two but
*three* different ways to do things:</p>
<p>1. instance methods: like MemorySegment::fill<br>
2. static methods: like MemorySegment::copy<br>
3. instance methods that are offset-friendly: like
MemorySegment::get</p>
<p>The presence of (3) is, I think, notable, because it reflects a
move that was also made by the ByteBuffer API. That give us some
hints about *one* possible way to bucketize the memory segment
functionalities:</p>
<p>* for unary methods (e.g. methods that work on a single segment),
use (3) - that is use an _instance_ method which takes an offset
(so that it makes the method more applicable w/o the need to
slice)<br>
* for binary methods, since there's two offsets, and the order of
arguments is always hard to get right, just use static methods;
this would apply to both copy and mismatch<br>
</p>
<p>While the above might not be perfect, I think there's an
appealing simplicity to it - and the explanation as to "why things
are the way they are" seems straightforward enough. There would be
the minor inconvenience of having to pass a zero offset in some
cases (which is already there for dereference methods), but
overall feels perhaps like a small price to pay.<br>
</p>
<p>Thoughts?<br>
</p>
<p>Maurizio<br>
<br>
[1] - <a class="moz-txt-link-freetext" href="https://bugs.openjdk.org/browse/JDK-8212619">https://bugs.openjdk.org/browse/JDK-8212619</a><br>
</p>
<br>
<p><br>
</p>
<div class="moz-cite-prefix">On 14/06/2022 00:42, Alexander Biryukov
wrote:<br>
</div>
<blockquote type="cite" cite="mid:CACRQ6qtPF_Fv41ZxvVG0gdamvJGndaximceWjGsaJVqEg3CV-A@mail.gmail.com">
<div dir="ltr">
<div>
There is a method MemorySegment#mismatch, but it's kind of
inefficient in the following situation:</div>
<br>
<div>
<span style="font-family:monospace">val seg1 =
mem.asSlice(offset, size)<br>
val seg2 = other.mem.asSlice(other.offset, other.size)<br>
return seg1.mismatch(seg2) == -1L</span>
</div>
<div><br>
</div>
<div>There are additional mismatch methods in java.util.Arrays
for arrays, like</div>
<div><br>
</div>
<span style="font-family:monospace">public static int
mismatch(byte[] a, int aFromIndex, int aToIndex,<br>
</span>
<div><span style="font-family:monospace">
byte[] b, int bFromIndex, int bToIndex)</span></div>
<div><span style="font-family:monospace"></span></div>
<div><span style="font-family:monospace"><br>
</span></div>
<div><span style="font-family:arial,sans-serif">It would be nice
to have something like that for MemorySegment as well.</span></div>
<div><span style="font-family:arial,sans-serif">Or even better
-- MemorySegment#compare :)</span></div>
<div><span style="font-family:monospace"><br>
</span></div>
<div>NB: is this the correct way to provide feedback? <br>
</div>
<div><br>
</div>
<div>Best regards,</div>
<div>Alexander Biryukov<br>
</div>
</div>
</blockquote>
</body>
</html>