scalar replacement of arrays affected by minor changes to surrounding code

Govind Jajoo gjajoo+java at gmail.com
Fri Sep 20 16:59:51 UTC 2019


Hi Roland,

Thanks for looking into this. Some replies inline

> it seems we could have it take care of fully unrolling the loop
Great!

> once null from the initialization and then the newly created
> array
I was afraid something like this might be happening and I threw in the
final keyword on the array, but apparently that didn't factored in the
analysis.

>  but zeroing elimination fails with this code shape
I take this to mean that there's no code shape with a newly allocated
wrapper object+array that C2 can elide allocations for.

>> It’s more like a nice bonus optimization: great if it
>> happens, Ok if not.
> sums it up very well.
Yep that's a good general summary. However it's nice to start with the most
idiomatic code possible and work backwards. While I was aware of the corner
cases around partial escape etc... I was quite surprised to see it fail for
seemingly vanilla code (what else do people do with arrays other than loop
over them? :-)). I hope EA is more of a "first-class" citizen with graal.
FWIW I've raised this issue with them -
https://github.com/oracle/graal/issues/1697.

Thanks,
Govind

On Fri, Sep 20, 2019 at 10:12 AM Roland Westrelin <rwestrel at redhat.com>
wrote:

>
> > public class ArrayLoop {
> >     private static Random s_r = new Random();
> >     private static int next() { return s_r.nextInt() % 1000; }
> >
> >     private static int loop(int... arr) {
> >         int sum = 0;
> >         for (int i = arr.length - 1; i >= 0; sum += arr[i--]) { ; }
> >         return sum;
> >     }
> >
> >     @Benchmark
> >     public void loopSum(Blackhole bh) {
> >         bh.consume(loop(next(), next()));
> >     }
> > }
>
> The problem with that one is that EA would need the loop to be fully
> unrolled to eliminate the allocation but that only happens after EA. So
> it's a pass ordering problem. We already run a pass of loop
> optimizations before EA so it seems we could have it take care of fully
> unrolling the loop.
>
> > scalar replacement kicks in as expected. Then to try out a more realistic
> > scenario representing our usage, we added the following wrapper and
> > benchmarks
> >
> >     private static class ArrayWrapper {
> >         final int[] arr;
> >         ArrayWrapper(int... many) { arr = many; }
> >         int loopSum() { return loop(arr); }
> >         int unrolledSum() { return unrolled(arr); }
> >     }
> >
> >     @Benchmark
> >     public void wrappedUnrolledSum(Blackhole bh) {
> >         bh.consume(new ArrayWrapper(next(), next()).unrolledSum());
> >     }
> >
> >     @Benchmark
> >     public void wrappedLoopSum(Blackhole bh) {
> >         bh.consume(new ArrayWrapper(next(), next()).loopSum());
> >     }
>
> EA doesn't eliminate the array allocation because it's assigned to a
> field and C2 sees 2 assignments to that field: once null from the
> initialization and then the newly created array. That's sufficient to
> make EA bail out. C2 would usually eliminate the zeroing from
> initialization so this would not be a problem but zeroing elimination
> fails with this code shape. I don't see an immediate way to fix this by
> either improving zeroing elimination or EA itself. It could well be that
> EA could proceed in this case and it's simply too conservative but
> making sure of that would require further investigation.
>
> Anyway, I think Vitaly's advice:
>
> > But beyond the intellectual curiosity angle behind your question, you
> > really shouldn’t rely on EA to recover performance (or GC pressure) in
> > critical scenarios.  It’s more like a nice bonus optimization: great if
> it
> > happens, Ok if not.
>
> sums it up very well.
>
> Roland.
>


More information about the hotspot-compiler-dev mailing list