[英]Java performance testing changes when executing code from callback class. Java stack frame issue?
我試圖編寫一個相當通用的測試框架來分析一組函數,但遇到了一個問題,我無法在下面詳細說明。
這個概念非常簡單。 我創建了一個Test
抽象類:
public abstract class Test {
private final String name;
public Test(String name) {
this.name = name;
}
public abstract void test();
}
然后,我有一個主測試類,包含一些配置信息和用於運行測試的循環。
public class MyTestClass {
public static double staticMethod1(Quat4f q) {
double angle;
float dirW = q.w;
if (q.w * q.w + q.y * q.y > 0.0f) {
if (q.w > 0.f && q.y < 0.0f)
dirW *= -1.0f;
angle = 2.0f * Math.acos(dirW);
} else {
angle = 0.0f;
}
return angle / 6.283f * 100.f;
}
public static double staticMethod2(Quat4f q) {
AxisAngle4f axisAngle = new AxisAngle4f();
axisAngle.set(q);
return axisAngle.angle / 6.283f * 100.f;
}
public static final void main(String[] args) {
final Quat4f quaternion = new Quat4f(0, 0, 0, 1);
Test[] tests = new Test[] {
new Test("staticMethod1") {
@Override
public void test() {
staticMethod1(quaternion);
}
},
new Test("staticMethod2") {
@Override
public void test() {
staticMethod2(quaternion);
}
}
};
long startTime = 0;
int repeat = 10; //How many times to repeat each iteration.
int[] tiers = new int[] { 1000, 10000, 100000, 1000000 };
long[][][] times = new long[tests.length][tiers.length][iterations];
for (int testIndex = 0; testIndex < tests.length; testIndex++) {
for (int tierIndex = 0; tierIndex < tiers.length; tierIndex++) {
for (int r = 0; r < repeat; r++) {
startTime = System.nanoTime();
for (int i = 0; i < tiers[tierIndex]; i++) {
tests[testIndex].run(); //run the test
}
times[testIndex][tierIndex][r] = System.nanoTime() - startTime; //Stash the execution time in the array.
}
}
}
}
}
查看此代碼,您可能想知道為什么Test.run()
方法調用靜態方法。 那是因為最初我只是將硬編碼調用嵌入到循環內部的靜態方法中。 我沒有調用tests[testIndex].run()
而是在那里顯式調用staticMethod1(quaternion)
。 這也意味着為每個方法冗余地復制這個循環代碼 - 這使我創建了抽象基類,這樣我就可以創建一個回調數組來測試。
因此,在切換到使用回調Test
類之后,我沒有明確地調用靜態函數,而是觀察了兩件事:
這是我明確調用靜態方法時的輸出表(所有時間都以納秒為單位):
1000 10000 100000 1000000
staticMethod1
315358 424208 1451141 14495864
125334 410525 1483797 14657896
125956 412079 1445543 14702681
150837 413012 1473845 14677179
126578 412080 1419419 14415313
126268 413323 1450830 14600361
125645 411457 1437147 14504261
126889 414257 1431548 14402563
129689 420476 1476954 14548734
848417 425453 1471046 14409715
staticMethod2
1528581 1031287 3137712 2180755
228899 303540 218947 2227406
228276 303228 218324 2232071
235118 301362 218324 2174224
226411 317534 218636 2180133
226410 302918 218946 2143434
253779 302606 219879 2116689
226099 349257 219880 2108913
240717 303228 218946 2150899
250358 303228 218946 2159607
這里是相同的代碼,只從我的抽象基類Test
類執行:
1000 10000 100000 1000000
staticMethod1
360453 454686 1985445 15699447
155191 449400 1639298 15048205
152391 449089 1576165 15128134
175095 451888 1537289 15300429
156746 466816 1600734 15190645
157989 464950 1641476 15483610
157368 452198 1559681 15316290
157990 460285 1572122 15402439
157367 527773 1538222 15078995
878274 454065 1548485 15077439
staticMethod2
1519562 1101263 1674130 8842756
274616 335883 1481309 8728930
285190 339616 1471046 8842135
291721 334950 1280089 8591155
294831 347391 1339491 13402065
332152 343970 1299683 10950426
300429 326553 1252100 7778814
285190 324999 1365615 8569385
297008 341792 1284133 7734030
283324 326554 1327984 11505256
我能夠形成的唯一猜想是,這可能與Java堆棧框架概念有關。 我真的在尋找能夠深入分析為什么會發生這種情況的人。
這些顯着不同的結果可能是什么結果?
我從家用電腦上重新測試了測試,所以我想我會重新發布這些測試結果以保證准確性。
1000 10000 100000 1000000
staticMethod1
629020 688864 3016204 40796517
348542 589891 2662401 39673949
355447 611921 3559403 39613447
416936 617511 4022701 39335929
412660 635267 4290355 38862108
409702 751996 4055583 38823967
405426 761202 4063803 38998238
410030 760545 4024016 39131407
411346 656640 3877366 38737489
1794991 723060 4139759 38286028
staticMethod2
2219818 4198617 2526272 15647236
735555 1939011 2651879 14482251
761860 445542 2480238 12990096
734569 222607 2437822 14278058
734898 264366 2323394 23561771
743118 220633 2739672 15669266
746734 224909 5159080 12916113
781918 223593 2342794 14843616
789481 229512 2740658 13400784
865108 227210 5202155 22015033
1000 10000 100000 1000000
staticMethod2
2159974 1598690 4343951 4011522
755284 484013 4646131 3989491
779945 460667 390302 4114111
866752 469874 413318 3833963
911141 495193 433376 4024016
878918 468230 424827 4162118
892070 452447 431074 3830346
806579 419894 463298 4003301
814142 424169 424826 3961871
830253 417593 432718 3823112
staticMethod1
768437 632637 4596480 38401771
421539 655325 3811603 37536663
418579 657626 3917481 37377517
425813 648091 3887230 37347924
423512 653023 3800095 38334692
428772 570820 3810288 37640568
435020 581013 3795162 37543896
426800 578382 3805027 37390670
448830 567861 4004617 37502466
1883443 663874 3848101 38096961
具有AxisAngle4f單實例化的回調(staticMethod1,staticMethod2)
1000 10000 100000 1000000
staticMethod1
693138 745420 4382752 26091003
405098 677355 3378227 41866476
390630 669793 4349213 42472807
430088 699057 4296931 27899147
385697 675711 4300549 42643790
382410 658941 4296603 32330563
393918 662888 2602557 42622417
380437 666833 2588747 32903026
393918 738515 2616367 26079823
1805843 679985 2570004 42191343
staticMethod2
444556 1640449 963422 8620168
463298 464942 946325 8545856
431732 474478 877931 8645487
452776 466915 870698 8761229
432718 449487 882534 8572490
443898 464613 876288 8482066
414633 538596 871684 8672121
408715 190054 876287 8626744
405427 96342 874643 8607016
436664 96343 847681 8543883
這些結果並非真正“戲劇化”。 1000000次運行的平均值為:
它們之間的減速(幾乎完全)偏離一個數量級這一事實很奇怪,但最重要的是回調方法最多減慢了約7 ns。 那不是很多。
更多結果已公布,與測試順序method2
則method1
,與以下一致:
Test
類的相同實現; 單態站點的速度優勢在於避免虛函數表查找,以及允許方法內聯發生。 你的方法都很短,很適合內聯。
您可以通過在從一個測試切換到另一個測試時使用--XX:+PrintCompilation
與println
結合來進一步驗證這種重新編譯只發生一次,但只發生一次。
您應該避免您的時間受到JVM的這些復雜性的影響。 為此,使用test(int iterations)
方法並將最內層的循環推入其中。 這將允許關鍵方法調度每次至少1000次迭代僅發生一次,並且變得無關緊要;
不信任任何時間測量低於1毫秒。 System.nanoTime
的精度遠不及1 ns:它的粒度通常約為1μs,為了獲得良好的精度,你必須遠高於此。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.