Skip to content

Review use of @inline methods in compiler/library #95

Open
@retronym

Description

@retronym

I noticed that mapConserve is now inlined (in 2.12.x). That is ostensibly a progression, as it is marked with the @inline annotation.

However, the benefit of devirtualizing the apply call within the iteration should be weighed against the cost of duplicated code and increased method size at the call site.

We should review such methods (perhaps looking first at the ones that are most frequently inlined now), and decide whether the annotation makes sense. I might also be possible to restructure the method internals so that some sections of code are factored into sub-methods marked @noinline.

We also now have the flexibility to control inlining with annotations at the call site (as tested in InlinerTest#inlineNoInlineOverride. This would allow us to, for example, remove @inline from mapConserve, but use args.mapConserve(_.typeSymbol) @inline if benchmarking suggests it is beneficial in some particular call sites.

Welcome to Scala 2.12.0-20160226-100424-bc25a72 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_71).
Type in expressions for evaluation. Or try :help.

⚡ scala -optimize -Yinline-warnings
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_71).
Type in expressions for evaluation. Or try :help.

scala> class Conserve { def foo(as: List[String]) = as.mapConserve(x => x) }
<console>:11: warning: Could not inline required method mapConserve because access level required by callee not matched by caller.
       class Conserve { def foo(as: List[String]) = as.mapConserve(x => x) }
                                                                  ^
<console>:11: warning: At the end of the day, could not inline @inline-marked method mapConserve
       class Conserve { def foo(as: List[String]) = as.mapConserve(x => x) }
                                                                  ^
defined class Conserve

scala> :javap Conserve#foo
  public scala.collection.immutable.List<java.lang.String> foo(scala.collection.immutable.List<java.lang.String>);
    descriptor: (Lscala/collection/immutable/List;)Lscala/collection/immutable/List;
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=11, args_size=2
         0: aload_1
         1: ifnonnull     6
         4: aconst_null
         5: athrow
         6: aconst_null
         7: aload_1
         8: aload_1
         9: astore        4
        11: astore_3
        12: astore_2
        13: aload         4
        15: invokevirtual #27                 // Method scala/collection/immutable/List.isEmpty:()Z
        18: ifeq          41
        21: aload_2
        22: ifnonnull     31
        25: aload_3
        26: astore        10
        28: goto          164
        31: aload_2
        32: aload_3
        33: invokevirtual #32                 // Method scala/collection/mutable/ListBuffer.prependToList:(Lscala/collection/immutable/List;)Lscala/collection/immutable/List;
        36: astore        10
        38: goto          164
        41: aload         4
        43: invokevirtual #36                 // Method scala/collection/immutable/List.head:()Ljava/lang/Object;
        46: astore        5
        48: aload         5
        50: checkcast     #38                 // class java/lang/String
        53: invokestatic  #42                 // Method $line4$$read$$iw$$iw$Conserve$$$anonfun$1:(Ljava/lang/String;)Ljava/lang/String;
        56: astore        6
        58: aload         6
        60: aload         5
        62: if_acmpne     82
        65: aload_2
        66: aload_3
        67: aload         4
        69: invokevirtual #45                 // Method scala/collection/immutable/List.tail:()Ljava/lang/Object;
        72: checkcast     #23                 // class scala/collection/immutable/List
        75: astore        4
        77: astore_3
        78: astore_2
        79: goto          13
        82: aload_2
        83: ifnonnull     96
        86: new           #29                 // class scala/collection/mutable/ListBuffer
        89: dup
        90: invokespecial #49                 // Method scala/collection/mutable/ListBuffer."<init>":()V
        93: goto          97
        96: aload_2
        97: astore        7
        99: aload_3
       100: astore        8
       102: aload         8
       104: aload         4
       106: if_acmpeq     133
       109: aload         7
       111: aload         8
       113: invokevirtual #36                 // Method scala/collection/immutable/List.head:()Ljava/lang/Object;
       116: invokevirtual #53                 // Method scala/collection/mutable/ListBuffer.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/ListBuffer;
       119: pop
       120: aload         8
       122: invokevirtual #45                 // Method scala/collection/immutable/List.tail:()Ljava/lang/Object;
       125: checkcast     #23                 // class scala/collection/immutable/List
       128: astore        8
       130: goto          102
       133: aload         7
       135: aload         6
       137: invokevirtual #53                 // Method scala/collection/mutable/ListBuffer.$plus$eq:(Ljava/lang/Object;)Lscala/collection/mutable/ListBuffer;
       140: pop
       141: aload         4
       143: invokevirtual #45                 // Method scala/collection/immutable/List.tail:()Ljava/lang/Object;
       146: checkcast     #23                 // class scala/collection/immutable/List
       149: astore        9
       151: aload         7
       153: aload         9
       155: aload         9
       157: astore        4
       159: astore_3
       160: astore_2
       161: goto          13
       164: aload         10
       166: areturn
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0     167     0  this   L$line4/$read$$iw$$iw$Conserve;
            0     167     1    as   Lscala/collection/immutable/List;
           97      67     7 mapConserve_loop$1_b   Lscala/collection/mutable/ListBuffer;
          100      64     8 mapConserve_loop$1_xc   Lscala/collection/immutable/List;
          149      15     9 mapConserve_loop$1_tail0   Lscala/collection/immutable/List;
           46     118     5 mapConserve_loop$1_head0   Ljava/lang/Object;
           56     108     6 mapConserve_loop$1_head1   Ljava/lang/Object;
           13     151     2 mapConserve_loop$1_mapped   Lscala/collection/mutable/ListBuffer;
           13     151     3 mapConserve_loop$1_unchanged   Lscala/collection/immutable/List;
           13     151     4 mapConserve_loop$1_pending   Lscala/collection/immutable/List;

Was:

Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_71).
Type in expressions for evaluation. Or try :help.

scala> class Conserve { def foo(as: List[String]) = as.mapConserve(x => x) }
defined class Conserve

scala> :javap Conserve#foo
  public scala.collection.immutable.List<java.lang.String> foo(scala.collection.immutable.List<java.lang.String>);
    descriptor: (Lscala/collection/immutable/List;)Lscala/collection/immutable/List;
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=2, args_size=2
         0: aload_1
         1: new           #9                  // class Conserve$$anonfun$foo$1
         4: dup
         5: aload_0
         6: invokespecial #13                 // Method Conserve$$anonfun$foo$1."<init>":(LConserve;)V
         9: invokevirtual #19                 // Method scala/collection/immutable/List.mapConserve:(Lscala/Function1;)Lscala/collection/immutable/List;
        12: areturn
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  this   LConserve;
            0      13     1    as   Lscala/collection/immutable/List;
      LineNumberTable:
        line 11: 0
    Signature: #39                          // (Lscala/collection/immutable/List<Ljava/lang/String;>;)Lscala/collection/immutable/List<Ljava/lang/String;>;

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions