Java中变量参数方法的性能

时间:2011-04-20 08:09:18

标签: java performance

我有一个关于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个参数的方法或只有一个带变量参数的方法?

6 个答案:

答案 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)

听说两个优化规则:

  • 不优化
  • (仅限专家!)不要优化

换句话说,从性能的角度来看,这不是你应该关心的。