循环内的局部变量和性能
總覽
有時(shí)會(huì)出現(xiàn)一個(gè)問題,即分配一個(gè)新的局部變量需要花費(fèi)多少工作。 我的感覺一直是,代碼已優(yōu)化到成本為靜態(tài)的程度,即一次執(zhí)行,而不是每次運(yùn)行時(shí)都執(zhí)行。
最近, Ishwor Gurung建議考慮將一些局部變量移出循環(huán)。 我懷疑這不會(huì)有所作為,但我從未測試過是否確實(shí)如此。
考試
這是我運(yùn)行的測試:
public static void main(String... args) {for (int i = 0; i < 10; i++) {testInsideLoop();testOutsideLoop();} }private static void testInsideLoop() {long start = System.nanoTime();int[] counters = new int[144];int runs = 200 * 1000;for (int i = 0; i < runs; i++) {int x = i % 12;int y = i / 12 % 12;int times = x * y;counters[times]++;}long time = System.nanoTime() - start;System.out.printf("Inside: Average loop time %.1f ns%n", (double) time / runs); }private static void testOutsideLoop() {long start = System.nanoTime();int[] counters = new int[144];int runs = 200 * 1000, x, y, times;for (int i = 0; i < runs; i++) {x = i % 12;y = i / 12 % 12;times = x * y;counters[times]++;}long time = System.nanoTime() - start;System.out.printf("Outside: Average loop time %.1f ns%n", (double) time / runs); }輸出以以下結(jié)尾:
內(nèi)部 :平均循環(huán)時(shí)間3.6 ns
外 :平均循環(huán)時(shí)間3.6 ns
內(nèi)部 :平均循環(huán)時(shí)間3.6 ns 外 :平均循環(huán)時(shí)間3.6 ns
將測試時(shí)間增加到1億次迭代,對結(jié)果的影響很小。
內(nèi)部 :平均循環(huán)時(shí)間3.8 ns
外 :平均循環(huán)時(shí)間3.8 ns
內(nèi)部 :平均循環(huán)時(shí)間3.8 ns 外 :平均循環(huán)時(shí)間3.8 ns
用>>, &, + I代替模和乘法
int x = i & 15; int y = (i >> 4) & 15; int times = x + y;版畫
內(nèi)部 :平均循環(huán)時(shí)間1.2 ns
外 :平均循環(huán)時(shí)間1.2 ns
內(nèi)部 :平均循環(huán)時(shí)間1.2 ns 外 :平均循環(huán)時(shí)間1.2 ns
盡管模量相對昂貴,但測試的分辨率為0.1 ns或小于時(shí)鐘周期的1/3。 這將顯示兩次測試之間的任何差異,以達(dá)到此精度。
使用卡尺
正如@maaartinus所評(píng)論的那樣, Caliper是一個(gè)微基準(zhǔn)測試庫,因此我對手工編寫代碼可能會(huì)慢得多感興趣。
public static void main(String... args) {Runner.main(LoopBenchmark.class, args); }public static class LoopBenchmark extends SimpleBenchmark {public void timeInsideLoop(int reps) {int[] counters = new int[144];for (int i = 0; i < reps; i++) {int x = i % 12;int y = i / 12 % 12;int times = x * y;counters[times]++;}}public void timeOutsideLoop(int reps) {int[] counters = new int[144];int x, y, times;for (int i = 0; i < reps; i++) {x = i % 12;y = i / 12 % 12;times = x * y;counters[times]++;}} }首先要注意的是,代碼較短,因?yàn)樗话ㄓ?jì)時(shí)和印刷樣板代碼。 運(yùn)行此程序,我將與第一個(gè)測試使用同一臺(tái)計(jì)算機(jī)。
0% Scenario{vm=java, trial=0, benchmark=InsideLoop} 4.23 ns; σ=0.01 ns @ 3 trials 50% Scenario{vm=java, trial=0, benchmark=OutsideLoop} 4.23 ns; σ=0.01 ns @ 3 trialsbenchmark?? ns linear runtime InsideLoop 4.23 ============================== OutsideLoop 4.23 =============================vm: java trial: 0用shift和and替換模數(shù)
0% Scenario{vm=java, trial=0, benchmark=InsideLoop} 1.27 ns; σ=0.01 ns @ 3 trials 50% Scenario{vm=java, trial=0, benchmark=OutsideLoop} 1.27 ns; σ=0.00 ns @ 3 trialsbenchmark?? ns linear runtime InsideLoop 1.27 ============================= OutsideLoop 1.27 ==============================vm: java trial: 0這與第一個(gè)結(jié)果是一致的,并且一次測試僅慢了0.4-0.6 ns。 (大約兩個(gè)時(shí)鐘周期),并且移位幾乎沒有差異,并且加上測試。 這可能是由于游標(biāo)卡尺對數(shù)據(jù)進(jìn)行采樣而不會(huì)改變結(jié)果的緣故。
毫無疑問,在運(yùn)行真實(shí)程序時(shí),由于微型程序會(huì)執(zhí)行更多操作,因此獲得的時(shí)間通常比微型基準(zhǔn)測試時(shí)間更長,因此緩存和分支預(yù)測并不理想。 對所花費(fèi)時(shí)間的一小部分過高估計(jì)可能更接近您在實(shí)際程序中所期望的時(shí)間。
結(jié)論
這向我表明,在這種情況下,沒有任何區(qū)別。 我仍然懷疑當(dāng)JIT編譯代碼時(shí)分配局部變量的成本不會(huì)一次,并且沒有考慮每個(gè)迭代的成本。
參考: 可以優(yōu)化同步嗎? 來自我們的JCG合作伙伴 Peter Lawrey,來自Vanilla Java博客。
翻譯自: https://www.javacodegeeks.com/2012/12/local-variables-inside-a-loop-and-performance.html
總結(jié)
以上是生活随笔為你收集整理的循环内的局部变量和性能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 石头推出 2 款 Q 系列扫地机器人,售
- 下一篇: 一键ghost恢复的是什么盘