Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Use a java static to throw IndexOutOfBoundsException #7086

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

Merged
merged 1 commit into from
Nov 2, 2018

Conversation

cb372
Copy link
Contributor

@cb372 cb372 commented Aug 16, 2018

Follow-up to #6972.

As suggested by @retronym, this reduces the bytecode fixed cost of the synthetic productElement and productElementName implementations in case classes.

Measured the difference using a simple case class:

case class FiveFields(a: Int, b: Int, c: Int, d: Int, e: Int)

Before (2.13.0-pre-b739165):

-rw-r--r--  1 chris  staff  2207 Aug 16 11:11 FiveFields$.class
-rw-r--r--  1 chris  staff  6291 Aug 16 11:11 FiveFields.class

After:

-rw-r--r--  1 chris  staff  2207 Aug 16 22:54 FiveFields$.class
-rw-r--r--  1 chris  staff  6186 Aug 16 22:54 FiveFields.class

So a saving of 105 bytes.

The new bytecode looks like this:

  public java.lang.String productElementName(int);
    Code:
       0: iload_1
       1: istore_2
       2: iload_2
       3: tableswitch   { // 0 to 4
                     0: 36
                     1: 41
                     2: 46
                     3: 51
                     4: 56
               default: 61
          }
      36: ldc           #104                // String a
      38: goto          71
      41: ldc           #105                // String b
      43: goto          71
      46: ldc           #106                // String c
      48: goto          71
      51: ldc           #107                // String d
      53: goto          71
      56: ldc           #108                // String e
      58: goto          71
      61: iload_1
      62: invokestatic  #101                // Method scala/runtime/Statics.ioobe:(I)Ljava/lang/Object;
      65: checkcast     #110                // class java/lang/String
      68: goto          71
      71: areturn

If we care about saving another 3 bytes, we could make the ioobe return String instead of having it generic. That happens to work because it's only called in these two places (productElement, which returns Any, and productElementName, which returns String) but it seems a bit naughty.

Also removed the synthetic productElementName for case objects. A bridge method is generated instead, but it's slightly smaller than the synthetic so we save about 16 bytes on case objects.

@scala-jenkins scala-jenkins added this to the 2.13.0-RC1 milestone Aug 16, 2018
def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = {
createMethod(name, List(IntTpe), returnType) { m =>
val arg0 = Ident(m.firstParam)
val default = DEFAULT ==> Throw(IndexOutOfBoundsExceptionClass.tpe_*, fn(arg0, nme.toString_))
val default = DEFAULT ==> callStaticsGenericMethod("aioobe", returnType)(arg0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should add a val aioobe = "aioobe" to nme in StdNames rather than calling newTermName every time.

Apply(TypeApply(gen.mkAttributedRef(method), List(TypeTree(returnType))), args.toList)
}

def callStaticsGenericMethod(name: String, returnType: Type)(args: Tree*): Tree =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and then you won't be needing this.

@olafurpg
Copy link
Contributor

Nice! Another possible avenue to shave off further bytes is to skip generating productElementName for case object. Case objects inevitably throw ArrayIndexOutOfBoundsException when productArity == 0 so the default implementation in Product should do the right thing.

@cb372
Copy link
Contributor Author

cb372 commented Aug 16, 2018

Just noticed that we should actually throw IndexOutOfBoundsException (not ArrayIndexOutOfBoundsException), to match what is thrown by the implementation in Product.

Fixing now.

@cb372 cb372 changed the title Use a java static to throw ArrayIndexOutOfBoundsException Use a java static to throw IndexOutOfBoundsException Aug 16, 2018
@@ -76,10 +76,15 @@ trait MethodSynthesis {
def forwardMethod(original: Symbol, newMethod: Symbol)(transformArgs: List[Tree] => List[Tree]): Tree =
createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident)))

def callStaticsGenericMethod(name: TermName, returnType: Type)(args: Tree*): Tree = {
Copy link
Contributor

@mkeskells mkeskells Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is args a vararg - its always one param?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method is based on this method in SyntheticMethods. Over there it made more sense for it to have varargs.

To be honest, this method is only used in one place so we could just inline it if you're worried about performance. I would either do that, or leave it as it is for the sake of readability, but I don't have a strong opinion either way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think inlining it would be too bad, personally.

@@ -307,13 +314,13 @@ trait SyntheticMethods extends ast.TreeDSL {
Any_equals -> (() => equalsDerivedValueClassMethod)
)

def caseClassMethods = productMethods ++ /*productNMethods ++*/ Seq(
def caseClassMethods = productClassMethods ++ /*productNMethods ++*/ Seq(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove the intermediate collections easily here either in this PR or another - just to remove some CPU/memory in the compile

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I'm poking at this file; there's some room for improvement

@lrytz
Copy link
Member

lrytz commented Nov 2, 2018

@cb372 I reviewed this and pushed my review feedback as a commit. If it LGTY we can squash and merge.

@lrytz lrytz self-assigned this Nov 2, 2018
@cb372
Copy link
Contributor Author

cb372 commented Nov 2, 2018

@lrytz Thanks for the feedback. Your changes LGTM.

The Travis build failed with a StackOverflowError in the typer. I'm not sure if that's caused by the changes in this PR? I just ran the tests locally and I couldn't reproduce it.

This reduces the bytecode cost of the synthetic `productElement` and
`productElementName` implementations in case classes.
@cb372
Copy link
Contributor Author

cb372 commented Nov 2, 2018

Squashed and rebased. Will see if Travis is any happier this time.

@lrytz
Copy link
Member

lrytz commented Nov 2, 2018

You can ignore travis for now, scala/scala-dev#570

@lrytz lrytz merged commit 30123af into scala:2.13.x Nov 2, 2018
@cb372 cb372 deleted the static-aioobe branch November 2, 2018 21:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants