Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/389.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在JVM中评测块的正确方法?_Java_Scala_Jvm - Fatal编程技术网

Java 在JVM中评测块的正确方法?

Java 在JVM中评测块的正确方法?,java,scala,jvm,Java,Scala,Jvm,我看到许多帖子建议使用类似于下面的代码来分析Scala中的方法: def timer[A](block: => A): A = { val startTime = System.currentTimeMillis() val ret = block println(s"${System.currentTimeMillis() - startTime}ms") ret } 基本原理是为块按名称传递。然而,当我尝试一个代码片段时,我发现结果不可信 ob

我看到许多帖子建议使用类似于下面的代码来分析Scala中的方法:

  def timer[A](block: => A): A = {
    val startTime = System.currentTimeMillis()
    val ret = block
    println(s"${System.currentTimeMillis() - startTime}ms")
    ret
  }
基本原理是为
按名称传递
。然而,当我尝试一个代码片段时,我发现结果不可信

object Euler006 extends App {
  def f1(n: Int): Long = {
    val numbers = 1 to n
    def square(n: Int) = n.toLong * n
    square(numbers.sum) - numbers.map(square).sum
  }

  def f2(n: Int): Long = (n.toLong - 1) * n / 2 * (n + 1) / 3 * (3 * n + 2) / 2

  {
    val r2 = timer(f2(10000))
    val r1 = timer(f1(10000))
    println(s"$r1 $r2")
  }

  System.gc()
  System.runFinalization()

  {
    val r1 = timer(f1(10000))
    val r2 = timer(f2(10000))
    println(s"$r1 $r2")
  }

}
输出:

57ms // line 1
19ms // line 2
2500166641665000 2500166641665000
7ms  // line 4
0ms  // line 5
2500166641665000 2500166641665000
显然,执行
f2
所需的时间应该很短,但是第1行输出
57ms
。我想这可能是因为JVM的初始化。OTOH,第2行和第4行也不同,尽管我尝试了垃圾收集(我们不能保证这一点,因为JVM有一些不确定性,但这就是我所能想到的)

这个示例非常简单,我应该多次运行结果以实际分析它们(如Python中的
timeit
模块)。但是,我不确定如何编写正确的计时器来消除/减轻输出中显示的潜在影响

更新: JVM初始化应该包括在内,因为如果我在开始之前添加类似于
timer({})
的东西,第1行的时间开销很快就会变成
0ms
(表明它花费的时间很少)

显然,f2的执行时间应该很短

这可能需要一些时间,但不需要一毫秒。你的计算可能只有几分之一毫秒。事实上,由于没有使用结果,代码可能会被丢弃

我建议您使用
System.nanoTime()
,并确保使用结果

我猜可能是因为JVM的初始化

第一次调用代码时,必须加载代码,很可能这就是您正在计时的内容

第2行和第4行也不同,尽管我尝试了垃圾收集

代码现在已加载。注意:如果您复制代码并运行另一个执行相同操作的方法,则可能会得到类似的计时

我应该多次运行结果以实际分析它们

我会忽略运行的前2秒,以确保代码已经预热,或者使用像JMH这样的微基准测试框架

印刷品

# JMH 1.11.2 (released 164 days ago, please consider updating!)
# VM version: JDK 1.8.0_45, VM 25.45-b02
# VM invoker: /mnt/opt/jdk1.8.0_45/jre/bin/java
# VM options: -Didea.launcher.port=7534 -Didea.launcher.bin.path=/mnt/opt/idea-IC-143.1821.5/bin -Dfile.encoding=UTF-8
# Warmup: 6 iterations, 1 s each
# Measurement: 20 iterations, 2 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: vanilla.java.jmh.CalcBenchmark.calc

# Run progress: 0.00% complete, ETA 00:00:46
# Fork: 1 of 1
# Warmup Iteration   1: 0.105 ops/ns
# Warmup Iteration   2: 0.156 ops/ns
# Warmup Iteration   3: 0.169 ops/ns
# Warmup Iteration   4: 0.167 ops/ns
# Warmup Iteration   5: 0.166 ops/ns
# Warmup Iteration   6: 0.165 ops/ns
Iteration   1: 0.169 ops/ns
Iteration   2: 0.166 ops/ns
Iteration   3: 0.165 ops/ns
Iteration   4: 0.168 ops/ns
Iteration   5: 0.163 ops/ns
Iteration   6: 0.159 ops/ns
Iteration   7: 0.162 ops/ns
Iteration   8: 0.166 ops/ns
Iteration   9: 0.169 ops/ns
Iteration  10: 0.166 ops/ns
Iteration  11: 0.169 ops/ns
Iteration  12: 0.162 ops/ns
Iteration  13: 0.166 ops/ns
Iteration  14: 0.167 ops/ns
Iteration  15: 0.166 ops/ns
Iteration  16: 0.169 ops/ns
Iteration  17: 0.166 ops/ns
Iteration  18: 0.165 ops/ns
Iteration  19: 0.170 ops/ns
Iteration  20: 0.164 ops/ns


Result "calc":
  0.166 ±(99.9%) 0.002 ops/ns [Average]
  (min, avg, max) = (0.159, 0.166, 0.170), stdev = 0.003
  CI (99.9%): [0.163, 0.168] (assumes normal distribution)


# Run complete. Total time: 00:00:47

Benchmark            Mode  Cnt  Score   Error   Units
CalcBenchmark.calc  thrpt   20  0.166 ± 0.002  ops/ns
Took an average of 10.2 ns
Took an average of 6.7 ns
Took an average of 4.7 ns
Took an average of 4.7 ns
Took an average of 4.6 ns
简言之,一旦预热,您的操作将需要约6纳秒或0.000006毫秒


没有JMH的更简单的基准可能是这样的。注意:我更信任JMH数字

public class SimpleCalcBenchmark {
    static int n = 10000;
    static final AtomicLong blackHole = new AtomicLong();

    public static void main(String[] args) throws RunnerException, IOException {
        for (int i = 0; i < 5; i++) {
            long start = System.nanoTime();
            long counter = 0;
            while (System.nanoTime() - start < 2e9) {
                for (int j = 0; j < 100; j++) {
                    blackHole.lazySet(calc());
                }
                counter += 100;
            }
            long time = System.nanoTime() - start;
            System.out.printf("Took an average of %.1f ns%n", (double) time/counter);
        }
    }

    public static long calc() {
        return (n - 1L) * n / 2 * (n + 1) / 3 * (3 * n + 2) / 2;
    }
}

所以我必须使用jmh或其他框架来对其进行基准测试?@HongxuChen我建议使用jmh,或遵循我建议的其他技巧。我要求使用一个轻量级技巧,因为我提到了执行,但似乎没有使用基准测试工具。我肯定会重复使用jmh的建议。您真的不应该尝试编写自己的基准测试代码。@AlBlue即使您认为自己知道如何编写微基准测试,我也会使用JMH进行验证,简而言之,始终运行JMH。
Took an average of 10.2 ns
Took an average of 6.7 ns
Took an average of 4.7 ns
Took an average of 4.7 ns
Took an average of 4.6 ns