`private[this] def` 什么时候比 `private def` 有性能优势?

When is `private[this] def` a performance advantage over `private def`?

private def 相比,写 private[this] def 在性能噪声比方面是否有意义?我知道 private[this] valprivate val 有所不同,因为前者允许 scalac 创建实际字段。但也许对于 def 来说没有什么区别?最后,private[this] var怎么样?

有一个very similar question,但它不包含有关性能的具体说明。

尝试编译而不是反编译(我用的是jd-gui)这段代码:

class PrivateDefs {
  import PrivateDefs._

  private def privateMethod(x: Int) = privateMethodInObject(x)

  private[this] def privateThisMethod(x: Int) = privateMethodInObject(x)
}

object PrivateDefs {

  private[this] val N = 1000000000L

  def main(args: Array[String]) = {
    var i = 0
    var start = System.currentTimeMillis()
    while (i < N) {
      privateMethodInObject(1)
      i += 1
    }
    println("private method: " + (System.currentTimeMillis() - start) + " ms")

    i = 0
    start = System.currentTimeMillis()
    while (i < N) {
      privateThisMethodInObject(1)
      i += 1
    }
    println("private[this] method: " + (System.currentTimeMillis() - start) + " ms")
  }

  private def privateMethodInObject(x: Int) = x

  private[this] def privateThisMethodInObject(x: Int) = x

}

privateMethodInObjectprivateThisMethodInObject 之间的差异仅在访问修饰符中的字节码中(私有与 public):

  public int org$test$PrivateDefs$$privateMethodInObject(int x)
  {
    return x;
  }

  private int privateThisMethodInObject(int x)
  {
    return x;
  }

为了比较性能,运行代码进行了10次左右。我在本地机器上的每个方法调用 10 亿次大约需要 500 毫秒。当你 运行 测试几次时,数字几乎是一样的。所以我猜你可以找到除性能之外的任何理由来选择一个选项而不是另一个选项。

简答

不,没有任何性能优势。 private defprivate[this] def 都被转换为字节码中的 privatepublic 方法,这取决于它们是否从不同的 class 调用,而不取决于它们的可见性在 Scala 中是。

理论

让我们从 Scala language specificationprivate[this] 的描述开始:

it can be accessed only from within the object in which it is defined. That is, a selection p.M is only legal if the prefix is this or O.this, for some class O enclosing the reference. In addition, the restrictions for unqualified private apply.

您可以看到规范只说明了语法上可接受或不可接受的内容。 privateprivate[this] 只能从相同 class 或内部 class 的实例中调用。在字节码中,您只能区分 class 级别的访问,而不是实例级别。因此,这两个选项在字节码中应该相同,Scala 仅在编译期间强制执行差异。

基本案例

首先,让我们看一个简单的例子:

class MyClass {
    private def privateDef(x: Int) = x
    private[this] def privateThisDef(x: Int) = x
}

这被翻译成字节码

public class MyClass {
   private int privateDef(int);
   private int privateThisDef(int);
   public MyClass();
}

如您所见,两种方法最终都是 private,因此从 JVM 的角度来看没有区别(例如关于内联、static/dynamic 绑定等)。

内classes

当我们添加内部 classes 时,这会发生什么变化?

class MyClass {
  private def privateDef(x: Int) = x
  private[this] def privateThisDef(x: Int) = x

  class MyInnerClass{
    MyClass.this.privateDef(1)
    MyClass.this.privateThisDef(2)
  }
}

这被翻译成

public class MyClass {
  public int MyClass$$privateDef(int);
  public int MyClass$$privateThisDef(int);
  public MyClass();
}
public class MyClass$MyInnerClass {
  public final MyClass $outer;
  public MyClass MyClass$MyInnerClass$$$outer();
  public MyClass$MyInnerClass(MyClass);
}

可以看到这次MyClass里面的两个方法都是public,这样里面的class就可以调用了。同样,privateprivate[this] 之间没有区别。

同伴

当私有方法可以从不同的 class 中调用时,Scala 又添加了一种特殊情况 - 当您从相应 class 中的伴随对象调用私有方法时。 MyClass 的伴生对象是字节码中名为 MyClass$ 的单独 class。在 companion 中调用 private 方法会跨越 class 边界,因此这种方法在字节码中将是 public。

您不能在 companion 外部调用 private[this] 方法,但这只是一个语法限制。无论您在 privateprivate[this] 之间进行选择,结果在字节码中都是相同的。

变量

vars 的行为似乎与 defs 的行为有些不同。这个class

class MyClass {
  private var privateVar = 0
  private[this] var privateThisVar = 0
  private var privateVarForInner = 0
  private[this] var privateThisForInner = 0

  class MyInnerClass{
    privateVarForInner = 1
    privateThisForInner = 1
  }
}

编译为

public class MyClass {
  private int privateVar;
  private int privateThisVar;
  private int MyClass$$privateVarForInner;
  public int MyClass$$privateThisForInner;
  // ...
}

然后内部 class 将 setter 用于 privateVar,将字段访问用于 privateThisVar。我不确定为什么 scalac 会这样,我在规范中没有找到任何内容。也许这是特定于实现的东西。

编辑: 根据要求,我创建了一个小型 JMH benchmark 比较获取和设置 private varprivate[this] var。结果?根据 JMH,所有操作都是 ≈ 10⁻⁸s。差异可以忽略不计,内部 classes 和 vars 的情况无论如何都很少见。