Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 586
0__ Avatar asked Apr 25 '16 19:04

0__


People also ask

What does it mean when a method is private?

When a method is private it means it can only be accessed by objects OF THE SAME CLASS.

What is private this in Scala?

In Scala,private[this] is object private,which makes sure that any other object of same class is unable to access private[this] members.

When should methods be public Java?

The rule is that a method should be made provided unless it is needed. One of the main reasons for this is that in a future release of an API etc., you can always make a private function public, but you can almost never make a previous public function private without breaking existing code. Save this answer.

How do you access private classes in Scala?

In Scala, a private top-level class is also accessible from nested packages. This makes sense, since the enclosing scope of both the private class and the nested package is the same (it's the package inside which both are defined), and private means that the class is accessible from anywhere within its enclosing scope.


1 Answers

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.

like image 130
Mifeet Avatar answered Nov 16 '22 00:11

Mifeet