我有一个关于Java中具有可变数量参数的方法性能的问题。
说我有以下两种选择:
public static final boolean isIn(int i, int v1, int v2) {
return (v1 == i) || (v2 == i);
}
public static final boolean isIn(int i, int... values) {
for (int v : values) {
if (i == v) {
return true;
}
}
return false;
}
现在主要问题出现在第一种方法的版本达到20,30或甚至50个参数时。现在这只是伤害了眼睛。好的,这是遗留代码,我想用唯一的一个变量参数方法替换所有它。
知道对性能的影响是什么?编译器是否有可能对第二种方法进行一些优化,以便它或多或少类似于第一种形式?
编辑:好的,也许我不够清楚。我对50个参数的方法没有性能问题。正如Peter Lawrey所说,这只是可读性。 如果我切换到具有可变数量参数的新方法,我想知道性能问题 换句话说:如果你关心性能,最好的方法是什么?有50个参数的方法或只有一个带变量参数的方法?
答案 0 :(得分:4)
我有同样的问题,转而进行实验。
public class ArgTest {
int summation(int a, int b, int c, int d, int e, int f) {
return a + b + c + d + e + f;
}
int summationVArgs(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}
return sum;
}
final static public int META_ITERATIONS = 200;
final static public int ITERATIONS = 1000000;
static public void main(String[] args) {
final ArgTest at = new ArgTest();
for (int loop = 0; loop < META_ITERATIONS; loop++) {
int sum = 0;
final long fixedStart = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
sum += at.summation(2312, 45569, -9816, 19122, 4991, 901776);
}
final long fixedEnd = System.currentTimeMillis();
final long vargStart = fixedEnd;
for (int i = 0; i < ITERATIONS; i++) {
sum += at.summationVArgs(2312, 45569, -9816, 19122, 4991, 901776);
}
final long vargEnd = System.currentTimeMillis();
System.out.printf("%03d:%d Fixed-Args: %d ms\n", loop+1, ITERATIONS, fixedEnd - fixedStart);
System.out.printf("%03d:%d Vargs-Args: %d ms\n", loop+1, ITERATIONS, vargEnd - vargStart);
}
System.exit(0);
}
}
如果在现代JVM(此处为1.8.0_20)上运行此代码,您将看到可变数量的参数会导致性能开销,也可能导致内存消耗。
我只发布前25次:
001:1000000 Fixed-Args: 16 ms
001:1000000 Vargs-Args: 45 ms
002:1000000 Fixed-Args: 13 ms
002:1000000 Vargs-Args: 32 ms
003:1000000 Fixed-Args: 0 ms
003:1000000 Vargs-Args: 27 ms
004:1000000 Fixed-Args: 0 ms
004:1000000 Vargs-Args: 22 ms
005:1000000 Fixed-Args: 0 ms
005:1000000 Vargs-Args: 38 ms
006:1000000 Fixed-Args: 0 ms
006:1000000 Vargs-Args: 11 ms
007:1000000 Fixed-Args: 0 ms
007:1000000 Vargs-Args: 17 ms
008:1000000 Fixed-Args: 0 ms
008:1000000 Vargs-Args: 40 ms
009:1000000 Fixed-Args: 0 ms
009:1000000 Vargs-Args: 89 ms
010:1000000 Fixed-Args: 0 ms
010:1000000 Vargs-Args: 21 ms
011:1000000 Fixed-Args: 0 ms
011:1000000 Vargs-Args: 16 ms
012:1000000 Fixed-Args: 0 ms
012:1000000 Vargs-Args: 26 ms
013:1000000 Fixed-Args: 0 ms
013:1000000 Vargs-Args: 7 ms
014:1000000 Fixed-Args: 0 ms
014:1000000 Vargs-Args: 7 ms
015:1000000 Fixed-Args: 0 ms
015:1000000 Vargs-Args: 6 ms
016:1000000 Fixed-Args: 0 ms
016:1000000 Vargs-Args: 141 ms
017:1000000 Fixed-Args: 0 ms
017:1000000 Vargs-Args: 139 ms
018:1000000 Fixed-Args: 0 ms
018:1000000 Vargs-Args: 106 ms
019:1000000 Fixed-Args: 0 ms
019:1000000 Vargs-Args: 70 ms
020:1000000 Fixed-Args: 0 ms
020:1000000 Vargs-Args: 6 ms
021:1000000 Fixed-Args: 0 ms
021:1000000 Vargs-Args: 5 ms
022:1000000 Fixed-Args: 0 ms
022:1000000 Vargs-Args: 6 ms
023:1000000 Fixed-Args: 0 ms
023:1000000 Vargs-Args: 12 ms
024:1000000 Fixed-Args: 0 ms
024:1000000 Vargs-Args: 37 ms
025:1000000 Fixed-Args: 0 ms
025:1000000 Vargs-Args: 12 ms
...
即使在最好的时候,Vargs-Args也从未降到0ms。
答案 1 :(得分:3)
编译器旁边没有优化。 JVM可以优化代码,但这两种方法不会执行彼此相似的任何操作。如果你有像isIn(i, 1,2,3,4,5,6,7,8,9 /* plus 40 more */)
这样的代码行,那么你需要担心的不仅仅是性能问题。我会首先担心可读性。
如果您担心性能,请将参数作为int[]
传递,并重复使用。
BTW查找大量int值的最有效方法是使用类似TIntHashSet的Set
答案 2 :(得分:2)
当您有探查器输出时,请回来说这是一个问题。在那之前,它是过早的优化。
答案 3 :(得分:2)
到@Canonical Chris
我不认为你考试中的问题来自变量论证。由于for loop
,函数sumationVArgs需要更多时间才能完成。
我创建了这个功能并添加到基准
int summationVArgs2(int... args) {
return args[0] + args[1] + args[2] + args[3] + args[4] + args[5];
}
这就是我所看到的:
028:1000000 Fixed-Args: 0 ms
028:1000000 Vargs-Args: 12 ms
028:1000000 Vargs2-Args2: 0 ms
功能中的for loop
&#34; summationVArgs&#34;被编译为比添加函数更多的操作。它包含add operation
来增加迭代器,check operation
来检查条件,branch operation
来循环和退出循环,所有这些都为每个循环执行一次,除了分支操作以退出循环。
抱歉我的英语不好。我跳,你可以理解我的英语:)
答案 4 :(得分:1)
与宣布
相同 isIn(int i, int[] values) {
但是,在调用方法时,将变量打包起来会有一些小的开销
答案 5 :(得分:1)
听说两个优化规则:
换句话说,从性能的角度来看,这不是你应该关心的。