AdoptOpenJDK / jitwatch

Log analyser / visualiser for Java HotSpot JIT compiler. Inspect inlining decisions, hot methods, bytecode, and assembly. View results in the JavaFX user interface.
Other
3.09k stars 439 forks source link

Investigate new LogCompilation tags missed_CHA_opportunity and inline_level_discount #246

Closed chriswhocodes closed 1 year ago

chriswhocodes commented 7 years ago

Check the HotSpot source code and find out what these new tags mean and if they can suggest any code improvements.

chriswhocodes commented 7 years ago

Interesting stuff!

Inline Level Discount

<inline_level_discount caller='930' callee='948'/>

This is an optimisation to prevent MethodHandle and other invokedynamic stuff counting against the max inlining depth. Each time one of these calls is encountered it increments an adjustment counter to the max inlining depth of the current inlining tree.

HotSpot source file: hotspot/src/share/vm/opto/bytecodeInfo.cpp

InlineTree *InlineTree::build_inline_tree_for_callee( ciMethod* callee_method, JVMState* caller_jvms, int caller_bci) {

  float recur_frequency = _site_invoke_ratio * compute_callee_frequency(caller_bci);

  // Attempt inlining.
  InlineTree* old_ilt = callee_at(caller_bci, callee_method);

  if (old_ilt != NULL) {
    return old_ilt;
  }

  int max_inline_level_adjust = 0;

  if (caller_jvms->method() != NULL) {

    if (caller_jvms->method()->is_compiled_lambda_form()) {
      max_inline_level_adjust += 1;  // don't count actions in MH or indy adapter frames
    } else if (callee_method->is_method_handle_intrinsic() ||
               callee_method->is_compiled_lambda_form()) {
      max_inline_level_adjust += 1;  // don't count method handle calls from java.lang.invoke implementation
    }

    if (max_inline_level_adjust != 0 && C->print_inlining() && (Verbose || WizardMode)) {
      CompileTask::print_inline_indent(inline_level());
      tty->print_cr(" \\-> discounting inline depth");
    }

    if (max_inline_level_adjust != 0 && C->log()) {
      int id1 = C->log()->identify(caller_jvms->method());
      int id2 = C->log()->identify(callee_method);
      C->log()->elem("inline_level_discount caller='%d' callee='%d'", id1, id2);
    }
  }

  InlineTree* ilt = new InlineTree(C, this, callee_method, caller_jvms, caller_bci, recur_frequency, _max_inline_level + max_inline_level_adjus
t);
  _subtrees.append(ilt);

  NOT_PRODUCT( _count_inlines += 1; )

  return ilt;
}

I will try to come up with a sandbox example to demonstrate this.

Missed CHA Opportunity

<missed_CHA_opportunity klass='847' method='959'/>

HotSpot source file: share/vm/opto/doCall.cpp

This LogCompilation tag is output when HotSpot reaches code that contains this comment:

// %%% Not yet implemented.  Would throw minor asserts at present.
// %%% The most common wins are already gained by +UseUniqueSubclasses.

My take on it is that a further CHA (Class Hierarchy Analysis) optimisation is possible here but it hasn't been implemented because it's a rare case and the major CHA wins are already covered. There is a VM switch UseUniqueSubclasses (default true) mentioned in the comment and it's definition (in share/vm/opto/c2_globals.hpp) is:

develop(bool, UseUniqueSubclasses, true,                                  \
       "Narrow an abstract reference to the unique concrete subclass")   \
// Identify possible target method and inlining style
ciMethod* Compile::optimize_inlining(ciMethod* caller, int bci, ciInstanceKlass* klass,
                                     ciMethod* callee, const TypeOopPtr* receiver_type,
                                     bool check_access) {
// ... SNIP ...

  ciMethod* cha_monomorphic_target = callee->find_monomorphic_target(calling_klass, klass, actual_receiver, check_access);

  if (cha_monomorphic_target != NULL) {

    assert(!cha_monomorphic_target->is_abstract(), "");

    // Look at the method-receiver type.  Does it add "too much information"?
    ciKlass*    mr_klass = cha_monomorphic_target->holder();

    const Type* mr_type  = TypeInstPtr::make(TypePtr::BotPTR, mr_klass);

    if (receiver_type == NULL || !receiver_type->higher_equal(mr_type)) {
      // Calling this method would include an implicit cast to its holder.
      // %%% Not yet implemented.  Would throw minor asserts at present.
      // %%% The most common wins are already gained by +UseUniqueSubclasses.
      // To fix, put the higher_equal check at the call of this routine,
      // and add a CheckCastPP to the receiver.

      if (TraceDependencies) {
        tty->print_cr("found unique CHA method, but could not cast up");
        tty->print("  method  = ");
        cha_monomorphic_target->print();
        tty->cr();
      }

      if (log() != NULL) {
        log()->elem("missed_CHA_opportunity klass='%d' method='%d'",
                       log()->identify(klass),
                       log()->identify(cha_monomorphic_target));
      }

      cha_monomorphic_target = NULL;
    }
  }

... SNIP ...

I'll try to identify which method calls are causing this LogCompilation output