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

时间:2016-04-25 19:48:09

标签: performance scala visibility

与仅private[this] def相比,写作private def在性能与噪音比方面是否有意义?据我所知,private[this] val超过private val会有所不同,因为前者允许scalac创建实际字段。但也许def它没有区别?最后,private[this] var怎么样?

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

2 个答案:

答案 0 :(得分:5)

简短回答

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

理论

让我们从Scala language specification关于private[this]的内容开始:

  

只能从定义它的对象中访问它。也就是说,选择p.M仅在前缀为this或O.this时才合法,对于包含引用的某些类O.此外,对不合格的私人申请的限制。

您可以看到规范只是说出语法上可接受的内容。 privateprivate[this]都只能从同一个类或内部类的实例中调用。在字节码中,您只能区分类级别的访问,而不是实例级别。因此,两个选项在字节码中应该相同,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的观点没有区别(例如关于内联,静态/动态绑定等)。

内部课程

添加内部类时,这会如何变化?

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中的两个方法都是公共的,所以内部类可以调用它们。同样,privateprivate[this]之间没有区别。

同伴

当一个私有方法可以从另一个类调用时,Scala增加了一个特殊情况 - 当你从它各自类中的一个伴随对象调用一个私有方法时。 MyClass的伴随对象将是字节码中名为MyClass$的单独类。在随播中调用private方法会跨越类边界,因此这种方法将在字节码中公开。

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

瓦尔

vars的行为似乎与defs有所不同。这个班级

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;
  // ...
}

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

编辑:根据请求,我创建了一个小型JMH benchmark,用于比较获取和设置private varprivate[this] var的效果。结果?根据JMH,所有操作均为≈ 10⁻⁸。差异可以忽略不计,内部类和var的情况很少见。

答案 1 :(得分:1)

尝试编译而不是反编译(我使用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

}

privateMethodInObject privateThisMethodInObject 之间的区别仅在于访问修饰符(私有与公共)中的字节码:

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

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

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