[讨论] 请教一个share/vm/oops下的代码做fast subtype check的问题

hellhell 2011-07-27
刚开始这个问题我是直接去私信问R大的,R大受了我不少私信轰炸,估计不胜其扰,所以还没有回我,我也考虑过OpenJDK的邮件组,但是稍为翻了下邮件存档,觉得这种新手问题大概会石沉大海,于是先发到这里,希望各位高手给点提示。

不熟悉或者忘记fast subtype check的兄弟们请看这里http://www.everbox.com/f/jkOyJVAg6Lp6TuUockIu3ccswh

我不大理解paper上的算法,作者给出checking secondary types方法的时候

s.is_subtype_of(T) := {
    if (S.cache == T) return true;
    if (S == T) return true;
    if ( S.scan_s_s_array(T) ) {
        S.cache = T
        return true;
    }
}


然后给出Combining the Checks的时候,就把这个对cache内容的检查去掉了,换成if (off != &cache) return false;一句,我理解这句是off != &cache的话,T根本不是一个secondary type,下面就没有必要查了,但是如果在线性扫描secondary type array前做一个对cache内容的检查,不会效率更高么?

S.is_subtype_of(T) := {
    int off = T.offset;
    if (T == S[off]) return true;
    if (off != &cache) return false;
    if (S == T) return true;
    if ( S.scan_s_s_array(T) ) {
        S.cache = T
        return true;
    }
    return false;
}


我在实际的HotSpot代码中也找了,从1.5.0到最新的,没有的确没有任何地方去读这个cache的值(不是offset!!!!),希望各位高手可以解惑,谢谢。
RednaxelaFX 2011-07-27
sorry…难怪我觉得还有啥漏了的事情。

以前做分享用的PPT里我写过,JDK 6的HotSpot里是这样做的(跟论文不完全一样):
S.is_subtype_of(T) := {
  int off = T.offset;
  if (S == T) return true;
  if (T == S[off]) return true;
  if (off != &cache) return false;
  if ( S.scan_secondary_subtype_array(T) ) {
    S.cache = T;
    return true;
  }
  return false;
}


注意,&cache是指cache的地址,不是cache的内容。

那么看看实际是怎么实现的。用x86版的解释器代码来看,代码在src/cpu/x86/vm/assembler_x86.cpp。
在解释器里,instanceof指令会使用下面的程序生成的代码:
void MacroAssembler::check_klass_subtype(Register sub_klass,
                           Register super_klass,
                           Register temp_reg,
                           Label& L_success) {
  Label L_failure;
  check_klass_subtype_fast_path(sub_klass, super_klass, temp_reg,        &L_success, &L_failure, NULL);
  check_klass_subtype_slow_path(sub_klass, super_klass, temp_reg, noreg, &L_success, NULL);
  bind(L_failure);
}

这里可以看到,整个检查是分为fast path和slow path两边的。

在fast path里,这段:
  // This check has worked decisively for primary supers.
  // Secondary supers are sought in the super_cache ('super_cache_addr').
  // (Secondary supers are interfaces and very deeply nested subtypes.)
  // This works in the same check above because of a tricky aliasing
  // between the super_cache and the primary super display elements.
  // (The 'super_check_addr' can address either, as the case requires.)
  // Note that the cache is updated below if it does not help us find
  // what we need immediately.
  // So if it was a primary super, we can just fail immediately.
  // Otherwise, it's the slow path for us (no success at this point).

  if (super_check_offset.is_register()) {
    local_jcc(Assembler::equal, *L_success);           // 这是if (T == S[off])后面要跳到return true的地方
    cmpl(super_check_offset.as_register(), sc_offset); // 这是off 跟 &cache在比较
    if (L_failure == &L_fallthrough) {
      local_jcc(Assembler::equal, *L_slow_path);       // 特殊情况直接跳去slow path继续
    } else {
      local_jcc(Assembler::notEqual, *L_failure);      // 这个是if (off != &cache)后面要跳到return false的地方
      final_jmp(*L_slow_path);
    }
  }

于是…就这样。那个cache的地址的作用被重载了。这种用法在论文里有写的,仔细读读应该能懂。
hellhell 2011-07-27
这里的确是paper上算法的实现,不过我觉得对cache地址的检查,只可以说明这个T是一个secondary type,所以对&cache检查结果为false了就立即返回失败。但是这里具体是什么type,就不能断定。比如一个对象,实现了多个interface,在check subtype时如果传入的T是interface,还是要去slow path线性扫描。

我的疑问是为什么不在进入slow path前先check下cache的内容?而且如果不用读cache的内容,那么把这个S.cache = T 移除不好么?(这个我还没有尝试过)
RednaxelaFX 2011-07-27
hellhell 写道
我的疑问是为什么不在进入slow path前先check下cache的内容?而且如果不用读cache的内容,那么把这个S.cache = T 移除不好么?(这个我还没有尝试过)

因为:
4.3 Combining the Checks 写道
But, as shown in Figure 2 below, if T is a restricted secondary type, its offset field holds the offset of cache, and so S[T.offset] is the same as S.cache.

S.cache的内容在fast path里还是check过的。只是跟S[off]合体了而已。
论文读仔细哦~
hellhell 2011-07-28
so S[T.offset] is the same as S.cache.
不好意思,我居然看漏了这一句,现在明白了,第一个检查就相当于对cache的检查。
感谢R大的帮助,打扰你这么多,不好意思。
Global site tag (gtag.js) - Google Analytics