为什么第一次运行总是慢得多? [英] Why is the first run always much slower?
问题描述
我编写了一个宏来报告运行给定操作所需的时间.它运行多次,并以纳秒为单位打印出每次运行的时间.第一次运行总是比后续运行花费更多的时间.为什么会这样?
I wrote a macro that reports the time required to run a given operation. It runs it a number of times and prints out the time for each run in nanoseconds. The first run always takes significantly more time than subsequent ones. Why is that so?
这是 10 x 10 次运行的结果,计时 Thread.yield()
:
Here are the results of 10 x 10 runs, timing Thread.yield()
:
> (dotimes [x 10] (prn (times 10 (Thread/yield))))
[55395 1659 622 561 591 702 795 719 742 624]
[3255 772 884 677 787 634 605 664 629 657]
[3431 789 965 671 774 767 627 627 521 717]
[2653 780 619 632 616 614 606 602 629 667]
[2373 759 700 676 557 639 659 654 659 676]
[2884 929 627 604 689 614 614 666 588 596]
[2796 749 672 769 667 852 629 589 627 802]
[1308 514 395 321 352 345 411 339 436 315]
[1390 363 328 337 330 321 324 347 333 342]
[1461 416 410 320 414 381 380 388 388 396]
第一批的第一次运行非常慢,我猜这是由于 JIT 第一次看到代码 - 足够公平.但是所有后续批次中的第一次运行也明显慢于后续运行.为什么?
The first run of the first batch is extremely slow, I guess that's due to the JIT seeing the code for the first time - fair enough. But the first runs in all subsequent batches are also significantly slower than following runs. Why?
times
宏的代码:
(defmacro time
[expr]
`(let [t1# (System/nanoTime)]
~expr
(- (System/nanoTime) t1#)))
(defmacro times
[reps expr]
`(loop [reps# ~reps times# []]
(if (zero? reps#)
times#
(recur (dec reps#) (conj times# (time ~expr))))))
反编译产生以下结果,所以 System.nanoTime()
似乎是在 Thread.yield()
之前和之后直接调用的,正如预期的那样:
Decompiling yields the following, so System.nanoTime()
seems to be called directly before and after Thread.yield()
, as intended:
> (decompile (dotimes [x 10] (prn (times 10 (Thread/yield)))))
...
public Object invoke() {
long reps__1952__auto__2355 = 10L;
Object times__1953__auto__2356 = PersistentVector.EMPTY;
while (reps__1952__auto__2355 != 0L) {
final long dec = Numbers.dec(reps__1952__auto__2355);
final IFn fn = (IFn)const__3.getRawRoot();
final Object o = times__1953__auto__2356;
times__1953__auto__2356 = null;
final long t1__1946__auto__2354 = System.nanoTime();
Thread.yield();
times__1953__auto__2356 = fn.invoke(o, Numbers.num(Numbers.minus(System.nanoTime(), t1__1946__auto__2354)));
reps__1952__auto__2355 = dec;
}
final Object o2 = times__1953__auto__2356;
times__1953__auto__2356 = null;
return o2;
}
推荐答案
第一次运行总是比后续运行花费更多的时间.为什么会这样?
The first run always takes significantly more time than subsequent ones. Why is that so?
基准测试结果中还有另一个棘手的依赖因素:I/O.尝试一些测试运行,返回时间向量而不是打印它们,您应该会看到与此更相符的结果:
There's another tricky dependency factoring into your benchmark results: I/O. Try a few test runs that return the timing vectors rather than print them, and you should see results more in line with this:
(for [_ (range 10)]
(times 10 (Thread/yield)))
=>
([32674 1539 1068 1063 1027 1026 1025 1031 1034 1035]
[1335 1048 1030 1036 1043 1037 1036 1031 1034 1047]
[1088 1043 1029 1035 1045 1035 1036 1035 1045 1047]
[1051 1037 1032 1031 1048 1045 1039 1045 1042 1037]
[1054 1048 1032 1036 1046 1029 1038 1038 1039 1051]
[1050 1051 1039 1037 1038 1035 1030 1030 1045 1031]
[1054 1045 1034 1034 1045 1037 1037 1035 1046 1044]
[1051 1041 1032 1050 1061 1039 1045 1041 1057 1034]
[1052 1042 1034 1032 1035 1045 1043 1038 1052 1052]
[1053 1053 1041 1043 1053 1044 1039 1042 1051 1038])
如果您在基准测试中使用 System.out.println
而不是 prn
,您应该会看到相同的减速行为,但没有那么夸张:
If you use System.out.println
in your benchmark instead of prn
, you should see the same slow-down behavior but much less exaggerated:
(dotimes [x 10]
(.println System/out (times 10 (Thread/yield))))
=> nil
[33521 1733 1232 1161 1150 1135 1151 1138 1143 1144]
[1724 1205 1149 1152 1141 1149 1149 1150 1139 1145]
[1368 1156 1141 1139 1147 1149 1141 1147 1141 1149]
[1306 1159 1150 1141 1150 1148 1147 1142 1144 1149]
[1329 1161 1155 1144 1140 1155 1151 1149 1149 1140]
[1319 1154 1140 1143 1147 1154 1156 1149 1148 1145]
[1291 1166 1164 1149 1140 1150 1140 1152 1141 1139]
[4482 1194 1148 1150 1137 1165 1163 1154 1149 1152]
[1333 1184 1162 1163 1138 1149 1150 1151 1137 1145]
[1318 1150 1144 1150 1151 1147 1138 1147 1143 1149]
这篇关于为什么第一次运行总是慢得多?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!