开发者

Java's varargs performance

Coding i came around to check for the vararg performance of Java.

I write following test code:

public class T {

    public static void main(String[] args) {

        int n = 100000000;
        String s1 = new String("");
        String s2 = new String("");
        String s3 = new String("");
        String s4 = new String("");
        String s5 = new String("");

        long t = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            foo();
        }
        System.err.println(System.currentTimeMillis() - t);


        t = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            baz(s1, s2, s3, s4, s5);
        }
        System.err.println(System.currentTimeMillis() - t);

        t = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            bar(s1, s2, s3, s4, s5);
        }
        System.err.println(System.currentTimeMillis() - t);

    }

    static void foo() {
    }

    static void bar(String a1, String a2, String a3, String a4, String a5) {
    }

    static void baz(String... a) {
    }
}

On my machine the average output is:

78
4696
78

Seems that pass variables to methods is at no cost ?! 开发者_开发百科Good !

But using varags is 60x slower ! Why ?

An explanation could be that the program must create the array on the heap and the time is spend by GC. But for less loops i still get as output:

0
62
0

What is spending this extra time for and anyway the compiler has all information to resolve this to a fix variable call ...

Its not my intention to optimize for that, but i found this curious ...

Update

I added a new test

t = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
    baz(s1);
}
System.err.println(System.currentTimeMillis() - t);

And this one argument version is still 30x slower. Maybe there is an ArrayList.toArray() behind the scene ?

So be aware of no-needed varags methods in your code and refactor to fix length. That could be a perfomance boost.


Static list of arguments is quite different from an array. When you pass them that way, compiler reserves space for the references and populates them when the method is called.

Varargs is an equivalent of array. To call such a method, it's necessary to create and populate array at run time. That's why you observe the difference.

String[] and String... are synonyms. If you compared them, you should see identical performance.


Using both the latest JRE6 and JRE7 I get different results than yours and they indicate that varargs are 5 times faster:

69
69
311

However, I would not jump to conclusions because this benchmark has several flaws: the parameters are not used in the function; the function doesn't do anything; the arguments have the same value. JIT can easily optimize this code and inline function calls. I modified your example to address the aforementioned obvious problems and got the following results:

627
7470
7844

The conclusion is: don't hesitate to use varargs. If your function is trivial then its call be inlined by the JIT, and if it's not then the overhead of varargs will likely be negligible.


Interesting problem !

This is just a guess : behind the scenes, Var-args are just arrays. Creating this implicit array and populating it with the var-args parameters may take some time; hence the performance hit. Well, I guess.


I refactored some of the code. I use int now instead of String cause strings end in a pool and are immutable, and I don't know how that influences the compiler optimizations.

Also I recreate the values so they won't be constants which might also influence compiler optimizations.

And I do something with the methods, also against compiler optimizations.

public class Test {


    static int n = 100_000_000;
    static int[] all_string = new int[n*1*5];
    static int all_strings_index = 0;



    public static void main(String[] args) {

        while(true) {
    
            all_strings_index = 0;
    
            int s1 = (int) System.nanoTime();
            int s2 = (int) System.nanoTime();
            int s3 = (int) System.nanoTime();
            int s4 = (int) System.nanoTime();
            int s5 = (int) System.nanoTime();
    
            long t = System.currentTimeMillis();
    
    
            t = System.currentTimeMillis();
            for (int i = 0; i < n; i++) {
                var(s1, s2, s3, s4, s5);
            }
            System.err.println("varargs    "+(System.currentTimeMillis() - t));
    
            all_strings_index = 0;
    
            t = System.currentTimeMillis();
            for (int i = 0; i < n; i++) {
                par(s1, s2, s3, s4, s5);
            }
            System.err.println("parameters "+(System.currentTimeMillis() - t));
    
    
            all_strings_index = 0;
    
            int[] arr = new int[] {s1, s2, s3, s4, s5};
    
            t = System.currentTimeMillis();
            for (int i = 0; i < n; i++) {
                var2(arr);
            }
            System.err.println("array      "+(System.currentTimeMillis() - t));
            System.err.println();
    
        }

    }

    static void par(int a1, int a2, int a3, int a4, int a5) {
        all_string[all_strings_index++] = a1;
        all_string[all_strings_index++] = a2;
        all_string[all_strings_index++] = a3;
        all_string[all_strings_index++] = a4;
        all_string[all_strings_index++] = a5;
    }

    static void var(int... a) {
        for (int s : a) {
            all_string[all_strings_index++] = s;
        }
    }

    static void var2(int[] a) {
        for (int s : a) {
            all_string[all_strings_index++] = s;
        }
    }
}
varargs    981
parameters 415
array      687

varargs    962
parameters 434
array      411

varargs    975
parameters 469
array      439

varargs    983
parameters 462
array      447

varargs    999
parameters 470
array      439

varargs    1018
parameters 475
array      455

varargs    1014
parameters 467
array      440

varargs are a lot slower.


javap -v Test.class produces the following text:

Classfile /Users/jing/code/github/my/java-projects/byte-code/test.class
  Last modified 2021-9-5; size 1758 bytes
  MD5 checksum 007113ca45d82f5f86a65725f28a560f
  Compiled from "Test.java"
public class Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #23.#46        // java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#47        // Test.all_strings_index:I
   #3 = Methodref          #48.#49        // java/lang/System.nanoTime:()J
   #4 = Methodref          #48.#50        // java/lang/System.currentTimeMillis:()J
   #5 = Fieldref           #22.#51        // Test.n:I
   #6 = Methodref          #22.#52        // Test.var:([I)V
   #7 = Fieldref           #48.#53        // java/lang/System.err:Ljava/io/PrintStream;
   #8 = Class              #54            // java/lang/StringBuilder
   #9 = Methodref          #8.#46         // java/lang/StringBuilder."<init>":()V
  #10 = String             #55            // varargs
  #11 = Methodref          #8.#56         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #12 = Methodref          #8.#57         // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
  #13 = Methodref          #8.#58         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #14 = Methodref          #59.#60        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #15 = Methodref          #22.#61        // Test.par:(IIIII)V
  #16 = String             #62            // parameters
  #17 = Methodref          #22.#63        // Test.var2:([I)V
  #18 = String             #64            // array
  #19 = Methodref          #59.#65        // java/io/PrintStream.println:()V
  #20 = Fieldref           #22.#66        // Test.all_string:[I
  #21 = Integer            100000000
  #22 = Class              #67            // Test
  #23 = Class              #68            // java/lang/Object
  #24 = Utf8               n
  #25 = Utf8               I
  #26 = Utf8               all_string
  #27 = Utf8               [I
  #28 = Utf8               all_strings_index
  #29 = Utf8               <init>
  #30 = Utf8               ()V
  #31 = Utf8               Code
  #32 = Utf8               LineNumberTable
  #33 = Utf8               main
  #34 = Utf8               ([Ljava/lang/String;)V
  #35 = Utf8               StackMapTable
  #36 = Class              #69            // "[Ljava/lang/String;"
  #37 = Class              #27            // "[I"
  #38 = Utf8               par
  #39 = Utf8               (IIIII)V
  #40 = Utf8               var
  #41 = Utf8               ([I)V
  #42 = Utf8               var2
  #43 = Utf8               <clinit>
  #44 = Utf8               SourceFile
  #45 = Utf8               Test.java
  #46 = NameAndType        #29:#30        // "<init>":()V
  #47 = NameAndType        #28:#25        // all_strings_index:I
  #48 = Class              #70            // java/lang/System
  #49 = NameAndType        #71:#72        // nanoTime:()J
  #50 = NameAndType        #73:#72        // currentTimeMillis:()J
  #51 = NameAndType        #24:#25        // n:I
  #52 = NameAndType        #40:#41        // var:([I)V
  #53 = NameAndType        #74:#75        // err:Ljava/io/PrintStream;
  #54 = Utf8               java/lang/StringBuilder
  #55 = Utf8               varargs
  #56 = NameAndType        #76:#77        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #57 = NameAndType        #76:#78        // append:(J)Ljava/lang/StringBuilder;
  #58 = NameAndType        #79:#80        // toString:()Ljava/lang/String;
  #59 = Class              #81            // java/io/PrintStream
  #60 = NameAndType        #82:#83        // println:(Ljava/lang/String;)V
  #61 = NameAndType        #38:#39        // par:(IIIII)V
  #62 = Utf8               parameters
  #63 = NameAndType        #42:#41        // var2:([I)V
  #64 = Utf8               array
  #65 = NameAndType        #82:#30        // println:()V
  #66 = NameAndType        #26:#27        // all_string:[I
  #67 = Utf8               Test
  #68 = Utf8               java/lang/Object
  #69 = Utf8               [Ljava/lang/String;
  #70 = Utf8               java/lang/System
  #71 = Utf8               nanoTime
  #72 = Utf8               ()J
  #73 = Utf8               currentTimeMillis
  #74 = Utf8               err
  #75 = Utf8               Ljava/io/PrintStream;
  #76 = Utf8               append
  #77 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #78 = Utf8               (J)Ljava/lang/StringBuilder;
  #79 = Utf8               toString
  #80 = Utf8               ()Ljava/lang/String;
  #81 = Utf8               java/io/PrintStream
  #82 = Utf8               println
  #83 = Utf8               (Ljava/lang/String;)V
{
  static int n;
    descriptor: I
    flags: ACC_STATIC

  static int[] all_string;
    descriptor: [I
    flags: ACC_STATIC

  static int all_strings_index;
    descriptor: I
    flags: ACC_STATIC

  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=6, locals=10, args_size=1
         0: iconst_0
         1: putstatic     #2                  // Field all_strings_index:I
         4: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
         7: l2i
         8: istore_1
         9: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
        12: l2i
        13: istore_2
        14: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
        17: l2i
        18: istore_3
        19: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
        22: l2i
        23: istore        4
        25: invokestatic  #3                  // Method java/lang/System.nanoTime:()J
        28: l2i
        29: istore        5
        31: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
        34: lstore        6
        36: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
        39: lstore        6
        41: iconst_0
        42: istore        8
        44: iload         8
        46: getstatic     #5                  // Field n:I
        49: if_icmpge     86
        52: iconst_5
        53: newarray       int
        55: dup
        56: iconst_0
        57: iload_1
        58: iastore
        59: dup
        60: iconst_1
        61: iload_2
        62: iastore
        63: dup
        64: iconst_2
        65: iload_3
        66: iastore
        67: dup
        68: iconst_3
        69: iload         4
        71: iastore
        72: dup
        73: iconst_4
        74: iload         5
        76: iastore
        77: invokestatic  #6                  // Method var:([I)V
        80: iinc          8, 1
        83: goto          44
        86: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
        89: new           #8                  // class java/lang/StringBuilder
        92: dup
        93: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
        96: ldc           #10                 // String varargs
        98: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       101: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
       104: lload         6
       106: lsub
       107: invokevirtual #12                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
       110: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       113: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       116: iconst_0
       117: putstatic     #2                  // Field all_strings_index:I
       120: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
       123: lstore        6
       125: iconst_0
       126: istore        8
       128: iload         8
       130: getstatic     #5                  // Field n:I
       133: if_icmpge     152
       136: iload_1
       137: iload_2
       138: iload_3
       139: iload         4
       141: iload         5
       143: invokestatic  #15                 // Method par:(IIIII)V
       146: iinc          8, 1
       149: goto          128
       152: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
       155: new           #8                  // class java/lang/StringBuilder
       158: dup
       159: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
       162: ldc           #16                 // String parameters
       164: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       167: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
       170: lload         6
       172: lsub
       173: invokevirtual #12                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
       176: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       179: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       182: iconst_0
       183: putstatic     #2                  // Field all_strings_index:I
       186: iconst_5
       187: newarray       int
       189: dup
       190: iconst_0
       191: iload_1
       192: iastore
       193: dup
       194: iconst_1
       195: iload_2
       196: iastore
       197: dup
       198: iconst_2
       199: iload_3
       200: iastore
       201: dup
       202: iconst_3
       203: iload         4
       205: iastore
       206: dup
       207: iconst_4
       208: iload         5
       210: iastore
       211: astore        8
       213: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
       216: lstore        6
       218: iconst_0
       219: istore        9
       221: iload         9
       223: getstatic     #5                  // Field n:I
       226: if_icmpge     240
       229: aload         8
       231: invokestatic  #17                 // Method var2:([I)V
       234: iinc          9, 1
       237: goto          221
       240: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
       243: new           #8                  // class java/lang/StringBuilder
       246: dup
       247: invokespecial #9                  // Method java/lang/StringBuilder."<init>":()V
       250: ldc           #18                 // String array
       252: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       255: invokestatic  #4                  // Method java/lang/System.currentTimeMillis:()J
       258: lload         6
       260: lsub
       261: invokevirtual #12                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
       264: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       267: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       270: getstatic     #7                  // Field java/lang/System.err:Ljava/io/PrintStream;
       273: invokevirtual #19                 // Method java/io/PrintStream.println:()V
       276: goto          0
      LineNumberTable:
        line 13: 0
        line 15: 4
        line 16: 9
        line 17: 14
        line 18: 19
        line 19: 25
        line 21: 31
        line 24: 36
        line 25: 41
        line 26: 52
        line 25: 80
        line 28: 86
        line 30: 116
        line 32: 120
        line 33: 125
        line 34: 136
        line 33: 146
        line 36: 152
        line 39: 182
        line 41: 186
        line 43: 213
        line 44: 218
        line 45: 229
        line 44: 234
        line 47: 240
        line 48: 270
        line 50: 276
      StackMapTable: number_of_entries = 7
        frame_type = 0 /* same */
        frame_type = 255 /* full_frame */
          offset_delta = 43
          locals = [ class "[Ljava/lang/String;", int, int, int, int, int, long, int ]
          stack = []
        frame_type = 250 /* chop */
          offset_delta = 41
        frame_type = 252 /* append */
          offset_delta = 41
          locals = [ int ]
        frame_type = 250 /* chop */
          offset_delta = 23
        frame_type = 253 /* append */
          offset_delta = 68
          locals = [ class "[I", int ]
        frame_type = 250 /* chop */
          offset_delta = 18

  static void par(int, int, int, int, int);
    descriptor: (IIIII)V
    flags: ACC_STATIC
    Code:
      stack=4, locals=5, args_size=5
         0: getstatic     #20                 // Field all_string:[I
         3: getstatic     #2                  // Field all_strings_index:I
         6: dup
         7: iconst_1
         8: iadd
         9: putstatic     #2                  // Field all_strings_index:I
        12: iload_0
        13: iastore
        14: getstatic     #20                 // Field all_string:[I
        17: getstatic     #2                  // Field all_strings_index:I
        20: dup
        21: iconst_1
        22: iadd
        23: putstatic     #2                  // Field all_strings_index:I
        26: iload_1
        27: iastore
        28: getstatic     #20                 // Field all_string:[I
        31: getstatic     #2                  // Field all_strings_index:I
        34: dup
        35: iconst_1
        36: iadd
        37: putstatic     #2                  // Field all_strings_index:I
        40: iload_2
        41: iastore
        42: getstatic     #20                 // Field all_string:[I
        45: getstatic     #2                  // Field all_strings_index:I
        48: dup
        49: iconst_1
        50: iadd
        51: putstatic     #2                  // Field all_strings_index:I
        54: iload_3
        55: iastore
        56: getstatic     #20                 // Field all_string:[I
        59: getstatic     #2                  // Field all_strings_index:I
        62: dup
        63: iconst_1
        64: iadd
        65: putstatic     #2                  // Field all_strings_index:I
        68: iload         4
        70: iastore
        71: return
      LineNumberTable:
        line 55: 0
        line 56: 14
        line 57: 28
        line 58: 42
        line 59: 56
        line 60: 71

  static void var(int...);
    descriptor: ([I)V
    flags: ACC_STATIC, ACC_VARARGS
    Code:
      stack=4, locals=5, args_size=1
         0: aload_0
         1: astore_1
         2: aload_1
         3: arraylength
         4: istore_2
         5: iconst_0
         6: istore_3
         7: iload_3
         8: iload_2
         9: if_icmpge     38
        12: aload_1
        13: iload_3
        14: iaload
        15: istore        4
        17: getstatic     #20                 // Field all_string:[I
        20: getstatic     #2                  // Field all_strings_index:I
        23: dup
        24: iconst_1
        25: iadd
        26: putstatic     #2                  // Field all_strings_index:I
        29: iload         4
        31: iastore
        32: iinc          3, 1
        35: goto          7
        38: return
      LineNumberTable:
        line 63: 0
        line 64: 17
        line 63: 32
        line 66: 38
      StackMapTable: number_of_entries = 2
        frame_type = 254 /* append */
          offset_delta = 7
          locals = [ class "[I", int, int ]
        frame_type = 248 /* chop */
          offset_delta = 30

  static void var2(int[]);
    descriptor: ([I)V
    flags: ACC_STATIC
    Code:
      stack=4, locals=5, args_size=1
         0: aload_0
         1: astore_1
         2: aload_1
         3: arraylength
         4: istore_2
         5: iconst_0
         6: istore_3
         7: iload_3
         8: iload_2
         9: if_icmpge     38
        12: aload_1
        13: iload_3
        14: iaload
        15: istore        4
        17: getstatic     #20                 // Field all_string:[I
        20: getstatic     #2                  // Field all_strings_index:I
        23: dup
        24: iconst_1
        25: iadd
        26: putstatic     #2                  // Field all_strings_index:I
        29: iload         4
        31: iastore
        32: iinc          3, 1
        35: goto          7
        38: return
      LineNumberTable:
        line 69: 0
        line 70: 17
        line 69: 32
        line 72: 38
      StackMapTable: number_of_entries = 2
        frame_type = 254 /* append */
          offset_delta = 7
          locals = [ class "[I", int, int ]
        frame_type = 248 /* chop */
          offset_delta = 30

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: ldc           #21                 // int 100000000
         2: putstatic     #5                  // Field n:I
         5: getstatic     #5                  // Field n:I
         8: iconst_1
         9: imul
        10: iconst_5
        11: imul
        12: newarray       int
        14: putstatic     #20                 // Field all_string:[I
        17: iconst_0
        18: putstatic     #2                  // Field all_strings_index:I
        21: return
      LineNumberTable:
        line 3: 0
        line 4: 5
        line 5: 17
}
SourceFile: "Test.java"

After checking the above javap output, the following conclusion can be drawn.

On the method definition side:

  • For loading parameters to the operand stack, par use iload. var(int...) use iaload. Executing iaload requires more instructions to prepare the operand stack than iload.
  • par executes all its instruction sequentially. var(int...) has jumps.

par's definition:

  static void par(int, int, int, int, int);
    descriptor: (IIIII)V
    flags: ACC_STATIC
    Code:
      stack=4, locals=5, args_size=5
         0: getstatic     #20                 // Field all_string:[I
         3: getstatic     #2                  // Field all_strings_index:I
         6: dup
         7: iconst_1
         8: iadd
         9: putstatic     #2                  // Field all_strings_index:I
        12: iload_0
        13: iastore
        14: getstatic     #20                 // Field all_string:[I
        17: getstatic     #2                  // Field all_strings_index:I
        20: dup
        21: iconst_1
        22: iadd
        23: putstatic     #2                  // Field all_strings_index:I
        26: iload_1
        27: iastore
        28: getstatic     #20                 // Field all_string:[I
        31: getstatic     #2                  // Field all_strings_index:I
        34: dup
        35: iconst_1
        36: iadd
        37: putstatic     #2                  // Field all_strings_index:I
        40: iload_2
        41: iastore
        42: getstatic     #20                 // Field all_string:[I
        45: getstatic     #2                  // Field all_strings_index:I
        48: dup
        49: iconst_1
        50: iadd
        51: putstatic     #2                  // Field all_strings_index:I
        54: iload_3
        55: iastore
        56: getstatic     #20                 // Field all_string:[I
        59: getstatic     #2                  // Field all_strings_index:I
        62: dup
        63: iconst_1
        64: iadd
        65: putstatic     #2                  // Field all_strings_index:I
        68: iload         4
        70: iastore
        71: return
      LineNumberTable:
        line 55: 0
        line 56: 14
        line 57: 28
        line 58: 42
        line 59: 56
        line 60: 71

var(int...)'s definition:

  static void var(int...);
    descriptor: ([I)V
    flags: ACC_STATIC, ACC_VARARGS
    Code:
      stack=4, locals=5, args_size=1
         0: aload_0
         1: astore_1
         2: aload_1
         3: arraylength
         4: istore_2
         5: iconst_0
         6: istore_3
         7: iload_3
         8: iload_2
         9: if_icmpge     38
        12: aload_1
        13: iload_3
        14: iaload
        15: istore        4
        17: getstatic     #20                 // Field all_string:[I
        20: getstatic     #2                  // Field all_strings_index:I
        23: dup
        24: iconst_1
        25: iadd
        26: putstatic     #2                  // Field all_strings_index:I
        29: iload         4
        31: iastore
        32: iinc          3, 1
        35: goto          7
        38: return
      LineNumberTable:
        line 63: 0
        line 64: 17
        line 63: 32
        line 66: 38
      StackMapTable: number_of_entries = 2
        frame_type = 254 /* append */
          offset_delta = 7
          locals = [ class "[I", int, int ]
        frame_type = 248 /* chop */
          offset_delta = 30

On the method invocation side:

  • par load the local variables to the operand stack with iload instruction. var(int...) first create an array and store the local variables to the array. The latter needs more instruction than the former.

par's invocation:

       136: iload_1
       137: iload_2
       138: iload_3
       139: iload         4
       141: iload         5
       143: invokestatic  #15                 // Method par:(IIIII)V

var(int...)'s invocation:

        52: iconst_5
        53: newarray       int
        55: dup
        56: iconst_0
        57: iload_1
        58: iastore
        59: dup
        60: iconst_1
        61: iload_2
        62: iastore
        63: dup
        64: iconst_2
        65: iload_3
        66: iastore
        67: dup
        68: iconst_3
        69: iload         4
        71: iastore
        72: dup
        73: iconst_4
        74: iload         5
        76: iastore
        77: invokestatic  #6                  // Method var:([I)V

The above conclusion is in accordance with the test result in https://stackoverflow.com/a/63121156/431698. And varargs performance problem is why some popular logging libraries such as Log4j2 and Flogger have versions that explicitly list message parameters besides the varargs version.

Flogger's LoggingApi has the following methods:

void log(String msg);
void log(String msg, @NullableDecl Object p1);
...
void log(
    String msg,
    @NullableDecl Object p1,
    @NullableDecl Object p2,
    @NullableDecl Object p3,
    @NullableDecl Object p4,
    @NullableDecl Object p5,
    @NullableDecl Object p6,
    @NullableDecl Object p7,
    @NullableDecl Object p8,
    @NullableDecl Object p9,
    @NullableDecl Object p10);
void log(
    String msg,
    @NullableDecl Object p1,
    @NullableDecl Object p2,
    @NullableDecl Object p3,
    @NullableDecl Object p4,
    @NullableDecl Object p5,
    @NullableDecl Object p6,
    @NullableDecl Object p7,
    @NullableDecl Object p8,
    @NullableDecl Object p9,
    @NullableDecl Object p10,
    Object... rest);

Log4j2's Logger has the following methods:

void info(String message)
void info(String message, Object p0)
void info(String message, Object p0, Object p1)
...
void info(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9)
void info(String message, Object... params)

For a discussion about Log4j2's varargs handling, see Why does Logger class provide a bunch of info methods beside the varargs mthod?


As said, an array is maintained when using var-args...,

you should also try to see the influence of adding "final" to the parameters of every methods

i personally get an improvement from 2250 -> 2234 ms for the array.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜