Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object methods/fields at static scope should be implemented as static #3545

Closed
scabug opened this issue Jun 8, 2010 · 4 comments
Closed

Comments

@scabug
Copy link

scabug commented Jun 8, 2010

(This suggestion originated on the mailing list. After some feedback, I converted it into a ticket)

This suggestion is a potential compiler optimization. If implemented properly, it should not change the behavior of any existing scala code, nor introduce any change in scala syntax.

Background:
The scala object system improves on java statics in that it allows for "scoped" statics. That is, singletons which can be globally scoped, scoped to a class, or even a method.

objects defined at the same scope as java statics are implemented using a static MODULE$$ instance of a generated version of the object class (i.e. an object called MyObject will be renamed MyObject$$. The compiler then generates a new class MyObject, and for every method in the original MyObject, the compiler generates a static method which forwards the call to the corresponding method on the static MODULE$$.

The MODULE$$ instance is instantiated in a static constructor section in MyObject$$, so the first code to reference an object method in MyObject will instantiate MODULE$$.

There are two problems with this:

The first is trivial. This implementation means that object methods defined at the same scope as static methods in java will always have a small amount of overhead. However, the overhead is small in and of itself.

The second, less trivial problem is that this implementation causes byte-code optimization tools, such as ProGuard, and possibly the VM itself, to consider every object method as a method which produces a global side effect, namely the instantiation of the static MODULE$$ field. Because of this, these tools consider it unsafe to perform additional optimizations on these methods.

For example, proguard can do algebraic reductions and peephole optimizations on bytecode. If it can inline adjacent calls to static methods (for example, various calls to java.Math static methods), it can perform these optimizations across method call boundaries.

However, because all scala object methods have at least one side-effect outside the body of the methods themselves, it cannot safely inline them, and therefor it cannot perform optimizations across adjacent method calls by inlining them and looking for further reductions. The potential performance gains of such optimizations are of course dependent on the code being optimized, but in some cases may be significant.

The suggestion:
Where it is safe to do so (when there is no externally visible side-effect other than the instantiation of MODULE$$), the scala compiler should implement the methods defined in objects at the static scope as static methods. The methods of the singleton's class would then forward those calls to the static methods, instead of the other way around.

Example:
Given the following scala code (a contrived example):

object MyObject {
  var field = 0
  def method = field * 2
}

Would be compiled into the following:

public final class MyObject {
  public static int field = 0;
  public static int field() { return field; }
  public static void field_eq$$(int value) { field = value; }
  public static int method() { return MyObject.field() * 2; }
}

public final class MyObject$$ extends ScalaObject {
  public static final MODULE$$;
  static { new(); }

  int field() { return MyObject.field(); }
  void field_eq$$(int value) { MyObject.field_eq$$(value); }
  int method() { return MyObject.method(); }
}

And, the following client code which uses MyObject:

object ClientCode {
  def clientMethod: Unit = {
    MyObject.field = MyObject.method
  }
}

Would be compiled down to:

public final class ClientCode {
  public static void clientMethod() {
    MyObject.field_eq$$(MyObject.method());
  }
}
// ClientCode$$ not shown

This optimization should be done on a case-by-case basis. For example, in the following code, method cannot be promoted to a static method, because the object's constructor has a visible side-effect:

object MyObjectWithSideEffect {
  // there is no benefit to putting this prinln in
  // a static section of the static MyObjectWithSideEffect class
  println("I'm a side effect!")

  // So this method might as well be implemented in the
  // MyObjectWithSideEffect$$ singleton class.
  def method = println("The above println must execute before I do")
}

Also, this would not apply to methods in objects defined at any other scope except for the static scope (although it may be possible to promote such a method to a static method, if it can be determined that it is side-effect free. However, that's beyond the scope of this suggestion).

And finally, if an object is defined as a subclass of another object (aside from ScalaObject, implicitly), or mixes in a trait, it may be more difficult to prove that it has no externally visible side-effects (you have to check each base class constructor). Not impossible, but not trivial either. As a short-cut, it is ok to simply turn off this optimization for methods defined in objects such as this:

object MyObject extends BaseClass with SomeTrait {...}
@scabug
Copy link
Author

scabug commented Jun 8, 2010

Imported From: https://issues.scala-lang.org/browse/SI-3545?orig=1
Reporter: Jeremy Bell (jeromiya)

@scabug
Copy link
Author

scabug commented Jun 29, 2010

@dragos said:
This is out of scope for a ticket, it is really a SID. It would be interesting to see this developed further, so please start a SID document.

@scabug
Copy link
Author

scabug commented May 18, 2011

DaveScala (davescala) said:
Will there also be a compiler option to turn this optimization off, for instance:[[BR]]
-nso (no static optimization)[[BR]]
[[BR]]
In this way it easier to compare performance and also to track in which part of the compiler a bug occurs.

@SethTisue
Copy link
Member

someone could revive this as a discussion on https://contributors.scala-lang.org

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants