<div dir="ltr">Hi,<div><br></div><div>Thanks a lot for your comment. Speaking from my experience, a frequent use case is that a loop (counted or not) calls into a function that cannot be inlined, and inside this function, there are a lot of bound checks, another use case is that the access indices are not trivial to derive from the induction variable. In the first case, the compiler will have a very hard time eliminating the checks, as from the perspective of the function, each check only occurs once, so there is nowhere to hoist to, but from the perspective of the program, those checks are executed inside its hot loop. And in the second case, there is nothing can be done. Just one particular data point, my current solution for 1brc using Unsafe results in around 1.1e12 instructions as reported by perf stat. I have tried implementing the same concept using VarHandle and FFM accessors, the instruction count was almost doubled at 2.1e12.</div><div><br></div><div>Your proposed solution still requires 2 checks, a lifetime check (albeit it can be hoisted, but not across function calls) and a positive index check, a workaround as you have proposed before is to create a segment on the fly and depend on the compiler to eliminate everything, the downside is that it is prone to the failure of inlining those function, which may be counter-productive. Furthermore, the most important downside is that all these tricks with MemorySegment will not work with Java arrays, which I think are the more common arguments of Unsafe methods.</div><div><br></div><div>Thanks a lot,</div><div>Quan Anh</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 12 Jan 2024 at 22:43, Maurizio Cimadamore <<a href="mailto:maurizio.cimadamore@oracle.com">maurizio.cimadamore@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Before I comment on your proposal, let's see what we can get through the <br>
means we have today.<br>
<br>
As you have tried to do, you can use MemorySegment, and its accessor. If <br>
your loops are counted, you should get loop unrolling, bound check <br>
elimination, and even auto-vectorization.<br>
<br>
The problem is when a loop is not counted, or runs for a number of <br>
iterations that is too small (so that even if checks are hoisted <br>
outside, you still execute them a lot).<br>
<br>
There is an escape hatch in the FFM API, one that requires <br>
"--enable-native-access". It goes like this:<br>
<br>
* Create an _everything_ segment, doing <br>
`MemorySegment.NULL.reinterpret(Long.MAX_SIZE)`<br>
* Stash the everything segment in a static final constant (so that JIT <br>
can see through its fields)<br>
* Tweak your code to operate on the everything segment - e.g. this <br>
becomes effectively a replacement for an Unsafe::getXYZ taking a long <br>
address<br>
<br>
Note: this is still not 100% on par with Unsafe, because FFM has still <br>
to check that the address you pass is positive. But this a much much <br>
simpler check.<br>
<br>
Hope this helps.<br>
<br>
Maurizio<br>
<br>
<br>
<br>
On 12/01/2024 14:14, Quân Anh Mai wrote:<br>
> Hi,<br>
><br>
> These days I have kept an eye on the 1brc challenge and 1 particular <br>
> phenomenon that has struck me is the usage of Unsafe by the <br>
> participants. While Java has developed a lot in terms of alternatives <br>
> for Unsafe such as VarHandle and FFM, a particular use case of Unsafe <br>
> is to access memory in an unsafe manner which cannot be done without <br>
> some kind of unsafe support.<br>
><br>
> I believe that while the compiler can theoretically eliminate a lot of <br>
> bound checks and even if they exist, a predictable branch is normally <br>
> cheap, there will always be places where that is not the case, as the <br>
> access can be very far from the place where the compiler can get the <br>
> relevant information, or the bound checks will compete with the <br>
> bottlenecked CPU frontend or backend. In the cases where every <br>
> nanosecond counts, a bound check may be prohibitively expensive, and <br>
> the capability to bypass them would be valuable.<br>
><br>
> Looking at other languages, C++ is unsafe by nature, C#, Go and even <br>
> Rust all provide the ability to step out of the safe realm. As a <br>
> result, I think the necessity of unsafe accesses is evident.<br>
><br>
> Regarding the implementation, the support can start minimally with the <br>
> ability to access arrays or to interpret an array as another without <br>
> bound checking. Unsafe accesses require the program to start with <br>
> --enable-unsafe-accesses=<module-name>, the same as how we restrict <br>
> native accesses, which IMO exhibits a similar nature after the <br>
> introduction of passing heap segment to native downcalls. Last but not <br>
> least, unsafe accesses will still perform bound checks prior to C2 or <br>
> in the presence of a special unsafe flag, the former acts as a safety <br>
> net that may catch most mistakes the programmers make and the latter <br>
> is a sanity check when the need arises.<br>
><br>
> Please let me know if there is any misunderstanding regarding the <br>
> situation, or if this is not the suitable mailing list for the proposal.<br>
><br>
> Regards,<br>
> Quan Anh<br>
</blockquote></div>