我看到很多帖子建议使用类似于下面的代码来分析Scala中的方法:
def timer[A](block: => A): A = {
val startTime = System.currentTimeMillis()
val ret = block
println(s"${System.currentTimeMillis() - startTime}ms")
ret
}
pass-by-name
的基本原理是block
。但是,当我尝试使用代码段时,我发现结果不可信。
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
(表明它花费的时间很少)
答案 0 :(得分:2)
显然f2应该花很少的时间来执行,
可能需要一些时间,但它不需要花费一毫秒。你的计算可能只有几毫秒。事实上,由于没有使用结果,代码可能会被丢弃。
我建议您使用System.nanoTime()
并确保使用结果。
猜测可能是因为JVM初始化。
第一次调用代码时,必须加载它,很可能这就是你的时间
第2行和第4行也不同,虽然我尝试了垃圾收集
代码已加载。注意:如果您复制代码并运行另一个执行相同操作的方法,则可能会得到类似的时间。
我应该多次运行结果来实际分析它们
我会忽略运行的前2秒以确保代码已经预热,或者使用像JMH这样的微基准框架http://openjdk.java.net/projects/code-tools/jmh/
static int n = 10000;
public static void main(String[] args) throws RunnerException, IOException {
long time = 2;
Options opt = new OptionsBuilder()
.include(CalcBenchmark.class.getSimpleName())
.warmupIterations(6)
.forks(1)
.measurementTime(TimeValue.seconds(time))
.timeUnit(TimeUnit.NANOSECONDS)
.build();
new Runner(opt).run();
}
@Benchmark
public long calc() {
return (n - 1L) * n / 2 * (n + 1) / 3 * (3 * n + 2) / 2;
}
打印
# 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
简而言之,一旦预热,您的操作应该需要大约6 ns或0.000006 ms
没有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;
}
}
打印
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