14

Does writing private[this] def make sense in terms of performance-to-noise ratio compared to just private def? I understand it makes a difference regarding private[this] val over private val because the former allows scalac to create an actual field. But perhaps for def it makes no difference? Finally, how about private[this] var?

There is a very similar question, but it does not contain specific statements regarding performance.

Mifeet
  • 12,949
  • 5
  • 60
  • 108
0__
  • 66,707
  • 21
  • 171
  • 266

2 Answers2

5

Short answer

No, there isn't any performance advantage. Both private def and private[this] def are translated to private or public methods in bytecode depending on whether they are called from a different class, not depending on what their visibility in Scala is.

Theory

Let's start with what the Scala language specification says about private[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.

You can see that the specification just says what is syntactically acceptable or not. Both private and private[this] can be only called from the instances of the same class or inner classes. In bytecode, you can only differentiate access on the class level, not an instance level. Therefore both options should be the same in bytecode and Scala enforces the difference only during compilation.

Basic case

First, let's look at a simple example:

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

This is translated to bytecode as

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

As you can see, both methods end up as private, therefore there is no difference from the JVM point of view (e.g. regarding inlining, static/dynamic binding, etc.).

Inner classes

How does this change when we add inner 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)
  }
}

This gets translated to

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);
}

You can see that this time, both methods in MyClass are public so that the inner class can call them. Again, no difference between private and private[this].

Companions

Scala adds one more special case when a private method can be called from a different class - when you call a private method from a companion object in its respective class. A companion object for MyClass would be a separate class named MyClass$ in bytecode. Calling a private method in companion crosses class boundaries and therefore such method will be public in bytecode.

You can't call private[this] method outside companion, but this is just a syntactical restriction. Wherever you can choose between private and private[this], the result will be the same in bytecode.

Vars

The behavior for vars seems to be somewhat different then for defs. This 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
  }
}

is compiled to

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

The inner class then uses setter for privateVar and field access for privateThisVar. I'm not sure why scalac behaves this way, I haven't find anything in the spec. Perhaps it's something implementation-specific.

Edit: Upon request, I created a small JMH benchmark comparing the performance of getting and setting a private var and private[this] var. The result? All operations are ≈ 10⁻⁸s according to JMH. The difference is negligible and the the case with inner classes and vars is rare anyway.

Mifeet
  • 12,949
  • 5
  • 60
  • 108
  • Agree, except "contrary to the other answer, there isn't any performance advantage", I thought it's written clear in other answer that there is not performance advantage. – Vitalii Kotliarenko May 07 '16 at 11:53
  • Although this is a great, elaborate answer on the difference between the two, and one can reflect from the nearly identical bytecode that this should pretty much have the same performance implications, still lacks the performance tests to back up that theory. – Yuval Itzchakov May 08 '16 at 07:46
  • The bytecode for defs is *identical*, verified by examining the class files. Do you really think it's worth measuring the performance of two identical bytecodes? Or did you mean measuring the case for `var` where the code is not identical? – Mifeet May 08 '16 at 07:50
  • @Mifeet I meant the places which aren't identical. As the question focuses mainly on the perf point of view. Of course there's no point testing identical byte code. – Yuval Itzchakov May 08 '16 at 13:24
  • Well, the only case which isn't identical is rather rare and difficult to measure, but see mu updated answer. The difference is below JMH's resolution. – Mifeet May 09 '16 at 15:59
1

Try to compile and than decompile (I used jd-gui) this code:

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

}

Difference between privateMethodInObject and privateThisMethodInObject in byte-code only in access modifier (private vs public):

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

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

To compare performance, run the code for about 10 times. I got about 500ms for 1 billions of each method call on my local machine. Numbers are pretty the same when you run test few times. So I guess you can find any reason except performance to choose one option over another.

Vitalii Kotliarenko
  • 2,947
  • 18
  • 26
  • Running a performance test like this is no good. See [this question](http://stackoverflow.com/q/504103/2032064) for an explanation how to properly measure performance with a microbenchmark. [JMH](http://openjdk.java.net/projects/code-tools/jmh/) is also a good starting point. – Mifeet May 07 '16 at 09:58
  • Also, this answer is wrong. The only reason why `privateMethodInObject` is public and `privateThisMethodInObject` is private is that you only call `privateMethodInObject` outside of of the object (i.e. in methods inside `class PrivateDefs`). See my answer for more explanation. – Mifeet May 07 '16 at 10:57