当从回调类执行代码时,Java性能测试会发生变化。Java堆栈框架问题?

当从回调类执行代码时,Java性能测试会发生变化。Java堆栈框架问题?,java,performance-testing,stack-frame,Java,Performance Testing,Stack Frame,我曾试图编写一个相当通用的测试框架来评测一组函数,但遇到了一个问题,我无法指出下面的详细内容 这个概念很简单。我创建了一个Test抽象类: public abstract class Test { private final String name; public Test(String name) { this.name = name; } public abstract void test(); } 然后,我有一个主测试类,带有一些配置信

我曾试图编写一个相当通用的测试框架来评测一组函数,但遇到了一个问题,我无法指出下面的详细内容

这个概念很简单。我创建了一个
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
类执行:

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堆栈框架概念有关。我真的在寻找一个能深入分析为什么会发生这种情况的人

这些截然不同的结果会是什么?


我在家里的电脑上重新运行了测试,所以我想我会重新发布这些结果以确保准确性

回调(staticMethod1、staticMethod2) 回调(staticMethod2、staticMethod1)
回调(staticMethod1、staticMethod2),只实例化AxisAngle4f

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次跑步的平均值为:

  • 静态方法1
    • 显式:14541456.7(14.5ns每次迭代)
    • 回调:15272563.3(15.2ns每次迭代)
    • 减速:每次迭代0.7纳秒
  • 静态方法2
    • 显式:2167413.1(2.2ns每次迭代)
    • 回调:94495.2(9.5ns每次迭代)
    • 减速:(每次迭代7纳秒)
事实上,它们之间的减速(几乎完全)相差一个数量级,这很奇怪,但重要的是回调方法的减速最多为7纳秒。这还不算太多

更新 您发布的更多结果(测试顺序为
method2
method1
)与以下内容一致:

  • JVM最初以解释模式运行代码,并对其进行分析
  • 它看到您总是调用
    Test
    类的相同实现
  • 所以它把这个调用位点编译成一个单态调用位点
  • 稍后,调用将更改为其他类。单态断言失败,导致立即重新编译巨态调用站点。JIT编译的代码中没有进行评测,因此不会重新访问调用站点,以发现它可以再次重新编译到专为新情况设计的单态站点
  • 单态站点的速度优势在于避免虚拟函数表查找,并允许方法内联。这两种方法都非常简短,很适合内联

    通过在从一个测试切换到另一个测试时使用
    --XX:+printcomilation
    println
    组合,可以进一步验证这种重新编译只发生一次

    进一步说明
  • 您应该避免定时受到JVM如此复杂的影响。为此,使用
    test(intiterations)
    方法并将最内部的循环推入其中。这将允许关键方法调度至少每1000次迭代只发生一次,并且变得无关紧要

  • 不信任任何低于1ms的时间测量。
    系统。nanoTime
    的准确度远不及1ns:其粒度通常在1µs左右,要获得良好的准确度,必须远远高于1ns


  • 这是非常好的手工制作基准。我不会仅从一般观点来攻击它。我尊重这些建议,但这个问题不是关于应用程序微观基准测试的最佳方法。这是关于理解为什么从调用静态方法到在回调类/函数中调用静态方法的结果发生了如此巨大的变化。从应用程序运行到应用程序运行,输出相对一致。+/-~每次重复的迭代次数为50000ns。请尝试以下方法:重新排序
    staticMethod1
    staticMethod2
    测试。看看开销的跳跃是否会停留在第二个测试上,不管该测试是什么。看起来
    method1
    没有受到太大的影响,而
    method2
    在第一次运行时要快得多(4ns vs 16ns)。所以,我猜是单态调用站点加内联的组合。当
    method2
    首先运行时,JIT首先发现它是单态的,然后意识到该方法很短,将其内联。如果您想知道如何避免这些干扰对函数性能的测量,那么我建议将最内部的循环推到回调中。它将导致重复,但是
    测试
    方法调度将至少每1000次迭代只发生一次。将所需的迭代次数传递给
    test()
    。顺便说一句,谷歌卡钳就是这样做的,不像甲骨文的jmh。我想你已经帮我找到了一个很好的理由回到卡钳:)请注意,OP首先完全测试一个代码路径,然后切换到另一个。这不应该让JVM陷入困境。@MarkoTopolnik我正在取消删除,因为你说我删除了它很遗憾。:)但我将把它编辑成一个社区维基,因为它是相当投机的。你已经有了一些重要的见解:每次呼叫的持续时间(我设法错误地计算了两个数量级:)和每次呼叫的开销(及其变化)。这是进一步得出结论的重要输入。我喜欢你的建议,即使用切换的两种方法重新运行。这可能是因为
    staticMethod1
    的减速只是内联差异,甚至是噪声(0.7ns根本不算多!),而
    staticMethod2
    的减速与JIT在看到一个非常确定的假设(单态调用)后感到困惑有关
    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    
    
    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