【问题标题】:Why this method does not get optimized away?为什么这种方法没有得到优化?
【发布时间】:2013-09-11 02:59:12
【问题描述】:

这个Java method 被用于模拟慢速计算的基准测试:

static int slowItDown() {
    int result = 0;
    for (int i = 1; i <= 1000; i++) {
        result += i;
    }
    return result;
}

恕我直言,这是一个非常糟糕的主意,因为它的主体可以替换为return 500500这似乎永远不会发生1;正如 Jon Skeet 所说,可能是因为这种优化与真实代码无关。

有趣的是,使用 result += 1; 的稍微简单的方法得到了完全优化(caliper 报告 0.460543 ns)。

但即使我们同意优化返回常量结果的方法对实际代码没有用处,仍然存在循环展开,这可能会导致类似

static int slowItDown() {
    int result = 0;
    for (int i = 1; i <= 1000; i += 2) {
        result += 2 * i + 1;
    }
    return result;
}

所以我的问题仍然存在:为什么这里没有进行优化?

1与我原来写的相反;我一定看到了什么不存在的东西。

【问题讨论】:

  • 你是如何测试这个的?如果使用 JIT,您可能会通过稍微更改代码来观察类似的事情,因为其中涉及很多启发式方法。即使是最简单的优化(如内联),您也无法保证 JIT 会应用,因为它只在认为必要时才会这样做。
  • 这是真实的代码吗?如果您知道返回的含义,为什么不直接这样编写代码呢?我很满意 JIT 编译器经过调整以优化 real 代码,而不是优化掉在现实中不会发生的代码。 (静态优化器比这有更多的余地,但请记住,JIT 编译器试图找到的每一个优化在执行时都有成本*。)
  • @JonSkeet:令人尴尬的是,我观察到该链接指向 Guava 基准测试代码,我通常希望信任它......
  • 返回 x + super.compareTo(e) - x; // 愚蠢的尝试阻止优化。所以他们似乎至少知道:)
  • @assylias:我无法重现方法被优化掉的情况!在我重写问题之前,我会再试一次。

标签: java guava compiler-optimization


【解决方案1】:

嗯,JVM确实优化掉了这样的代码。问题是在以这种方式分析之前,它必须被检测为真正的热点多少次(基准通常比这种单一方法做得更多)。在我的设置中,它需要 16830 次调用才能使执行时间(几乎)为零。

这样的代码不会出现在实际代码中是正确的。然而,在其他热点的几次内联操作后,它可能仍然存在,处理的值不是编译时常量,而是运行时常量或事实上的常量(理论上可以改变但实际上不会改变的值)。当这样一段代码仍然存在时,完全优化它是一个很大的好处,但预计不会很快发生,即直接从 main 方法调用时。

更新:我简化了代码,优化来得更早。

public static void main(String[] args) {
  final int inner=10;
  final float innerFrac=1f/inner;
  int count=0; 
  for(int j=0; j<Integer.MAX_VALUE; j++) {
    long t0=System.nanoTime();
    for(int i=0; i<inner; i++) slowItDown();
    long t1=System.nanoTime();
    count+=inner;
    final float dt = (t1-t0)*innerFrac;
    System.out.printf("execution time: %.0f ns%n", dt);
    if(dt<10) break;
  }
  System.out.println("after "+count+" invocations");
  System.out.println(System.getProperty("java.version"));
  System.out.println(System.getProperty("java.vm.version"));
}
static int slowItDown() {
  int result = 0;
  for (int i = 1; i <= 1000; i++) {
      result += i;
  }
  return result;
}

execution time: 0 ns
after 15300 invocations
1.7.0_13
23.7-b01

(64 位服务器虚拟机)

【讨论】:

  • 您能发布您的代码吗?我很好奇它是如何被优化掉的——我写道,我认为这也发生在我身上,但现在我想我看到了一些不存在的东西。
  • 看起来该方法得到了优化远离而不是优化(即折叠为常数)。我尝试了x += slowItDown() 之类的方法,最后打印了x,时间从未低于300 ns。这与你写的相对应;只是我对折叠更好奇(因为这会破坏我采用该方法的基准)。
猜你喜欢
  • 2015-10-06
  • 2015-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多