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
useiload
.var(int...)
useiaload
. Executingiaload
requires more instructions to prepare the operand stack thaniload
. 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 withiload
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.
精彩评论