RFR: 8279888: Local variable independently used by multiple loops can interfere with loop optimizations

Tobias Hartmann thartmann at openjdk.java.net
Wed Feb 9 07:29:09 UTC 2022


On Fri, 4 Feb 2022 14:41:55 GMT, Roland Westrelin <roland at openjdk.org> wrote:

> The bytecode of the 2 methods of the benchmark is structured
> differently: loopsWithSharedLocal(), the slowest one, has multiple
> backedges with a single head while loopsWithScopedLocal() has a single
> backedge and all the paths in the loop body merge before the
> backedge. loopsWithSharedLocal() has its head cloned which results in
> a 2 loops loop nest.
> 
> loopsWithSharedLocal() is slow when 2 of the backedges are most
> commonly taken with one taken only 3 times as often as the other
> one. So a thread executing that code only runs the inner loop for a
> few iterations before exiting it and executing the outer loop. I think
> what happens is that any time the inner loop is entered, some
> predicates are executed and the overhead of the setup of loop strip
> mining (if it's enabled) has to be paid. Also, if iteration
> splitting/unrolling was applied, the main loop is likely never
> executed and all time is spent in the pre/post loops where potentially
> some range checks remain.
> 
> The fix I propose is that ciTypeFlow, when it clone heads, not only
> rewires the most frequent loop but also all this other frequent loops
> that share the same head. loopsWithSharedLocal() and
> loopsWithScopedLocal() are then fairly similar once c2 parses them.
> 
> Without the patch I measure:
> 
> LoopLocals.loopsWithScopedLocal      mixed  avgt    5  1108.874 ± 250.463  ns/op
> LoopLocals.loopsWithSharedLocal      mixed  avgt    5  1575.665 ±  70.372  ns/op
> 
> with it:
> 
> LoopLocals.loopsWithScopedLocal      mixed  avgt    5  1108.180 ± 245.873  ns/op
> LoopLocals.loopsWithSharedLocal      mixed  avgt    5  1234.665 ± 157.912  ns/op
> 
> But this patch also causes a regression when running one of the
> benchmarks added by 8278518. From:
> 
> SharedLoopHeader.sharedHeader  avgt    5  505.993 ± 44.126  ns/op
> 
> to:
> 
> SharedLoopHeader.sharedHeader  avgt    5  724.253 ± 1.664  ns/op
> 
> The hot method of this benchmark used to be compiled with 2 loops, the
> inner one a counted loop. With the patch, it's now compiled with a
> single one which can't be converted into a counted loop because the
> loop variable is incremented by a different amount along the 2 paths
> in the loop body. What I propose to fix this is to add a new loop
> transformation that detects that, because of a merge point, a loop
> can't be turned into a counted loop and transforms it into 2
> loops. The benchmark performs better with this:
> 
> SharedLoopHeader.sharedHeader  avgt    5  567.150 ± 6.120  ns/op
> 
> Not quite on par with the previous score but AFAICT this is due to
> code generation not being as good (the loop head can't be aligned in
> particular).
> 
> In short, I propose:
> 
> - changing ciTypeFlow so that, when it pays off, a loop with
> multiple backedges is compiled as a single loop with a merge point in
> the loop body
> 
> - adding a new loop transformation so that, when it pays off, a loop
> with a merge point in the loop body is converted into a 2 loops loop
> nest, essentially the opposite transformation.

I executed some testing and I'm seeing massive (> 700) failures due to the `negative trip count?` assert. For example, `compiler/c2/Test6603011.java` with `-server -Xmixed`:

# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (.../open/src/hotspot/share/opto/loopPredicate.cpp:1447), pid=11690, tid=11708
#  assert(!follow_branches || loop_trip_cnt >= 0) failed: negative trip count?
#
# JRE version: Java(TM) SE Runtime Environment (19.0) (fastdebug build 19-internal+0-2022-02-08-1039057.tobias.hartmann.jdk2)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (fastdebug 19-internal+0-2022-02-08-1039057.tobias.hartmann.jdk2, compiled mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0x135c470]  PhaseIdealLoop::loop_predication_impl(IdealLoopTree*) [clone .part.0]+0xf70

Current CompileTask:
C2:  10125 4309    b  4       jdk.internal.org.objectweb.asm.ClassReader::readUtf (161 bytes)

Stack: [0x00007f83f4c3a000,0x00007f83f4d3b000],  sp=0x00007f83f4d34c60,  free space=1003k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x135c470]  PhaseIdealLoop::loop_predication_impl(IdealLoopTree*) [clone .part.0]+0xf70
V  [libjvm.so+0x135c739]  IdealLoopTree::loop_predication(PhaseIdealLoop*)+0x109
V  [libjvm.so+0x13a4b47]  PhaseIdealLoop::build_and_optimize(LoopOptsMode)+0x1327
V  [libjvm.so+0xa9cbea]  PhaseIdealLoop::optimize(PhaseIterGVN&, LoopOptsMode)+0x28a
V  [libjvm.so+0xa99513]  Compile::Optimize()+0x1193
V  [libjvm.so+0xa9b5f5]  Compile::Compile(ciEnv*, ciMethod*, int, Options, DirectiveSet*)+0x1575
V  [libjvm.so+0x8b1e29]  C2Compiler::compile_method(ciEnv*, ciMethod*, int, bool, DirectiveSet*)+0x669
V  [libjvm.so+0xaab4d8]  CompileBroker::invoke_compiler_on_method(CompileTask*)+0xc88
V  [libjvm.so+0xaac2b8]  CompileBroker::compiler_thread_loop()+0x668
V  [libjvm.so+0x19382da]  JavaThread::thread_main_inner()+0x25a
V  [libjvm.so+0x1940620]  Thread::call_run()+0x100
V  [libjvm.so+0x1623cd4]  thread_native_entry(Thread*)+0x104

-------------

PR: https://git.openjdk.java.net/jdk/pull/7352


More information about the hotspot-compiler-dev mailing list