0

我正在测试方法句柄(随 Java 7 提供)对 Java 反射和镜像(与反射一起使用的流畅 API - http://projetos.vidageek.net/mirror/mirror/ )有多少性能。所以我不知道我是否正确编写了测试代码。

我使用 JHM 工具进行此测试,并在我的要点中分享了代码和结果:https ://gist.github.com/garcia-jj/057dcab7f388e5cb42d1

我在方法中缓存了setup所有查找以提高性能。

反射和方法句柄之间的最后时间几乎相等。

所以我的问题是:我的测试对吗?在我的应用程序中缓存方法查找存在一些问题?或者我需要在需要使用时始终查找invokeExact?如果我不创建缓存,则性能太低。

谢谢

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class Reflecting {
    // testing method handle
    private MethodHandle mhConcat;
    private MethodHandle mhHashCode;

    // testing reflection     
    private Method rconcat;
    private Method rhashcode;

    // testing mirror api
    private MethodHandler mrConcat;
    private MethodHandler mrHashcode;

    @Setup
    public void setup() throws Exception {
        mhConcat = publicLookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
        mhHashCode = publicLookup().findVirtual(Object.class, "hashCode", methodType(int.class));

        rconcat = String.class.getDeclaredMethod("concat", String.class);
        rhashcode = String.class.getDeclaredMethod("hashCode");

        mrConcat = new Mirror().on((Object) "x").invoke().method("concat");
        mrHashcode = new Mirror().on((Object) "xy").invoke().method("hashCode");
    }

    @GenerateMicroBenchmark
    public void invoke(BlackHole bh) throws Throwable {
        bh.consume((String) mhConcat.invokeExact("x", "y"));
        bh.consume((int) mhHashCode.invokeExact((Object) "xy"));
    }

    @GenerateMicroBenchmark
    public void reflect(BlackHole bh) throws Throwable {
        bh.consume(rconcat.invoke("x", "y"));
        bh.consume(rhashcode.invoke("xy"));
    }

    @GenerateMicroBenchmark
    public void mirror(BlackHole bh) throws Throwable {
        bh.consume(mrConcat.withArgs("y"));
        bh.consume(mrHashcode.withoutArgs());
    }
}
4

1 回答 1

1

好吧,“错误”的定义通常从“正确”的定义开始。假设您想通过不同的方式测量目标方法的调用成本,我可以列举一些可能需要注意的事情:

  1. @GenerateMicroBenchmark建议您使用相当过时的 JMH(它已重命名为@Benchmark几个月前)。考虑更新以获得更可靠的线束版本。

  2. -s的使用Blackhole是光荣的,但为什么你每次有两个方法调用@Benchmark?您不想单独量化每个特定调用的性能吗?否则,您可能会用另一种方法的性能下降来掩盖一种方法的性能改进。拆分方法也有助于使用隐式Blackhole-s(即返回结果@Benchmark而不是使用显式Blackhole)。

  3. 基准测试未能阻止调用参数是可预测的,这可能会使一些测试在被调用者内部进行常量折叠。参见例如JMHSample_10_ConstantFold.java

但实际上,最大的问题是盲目相信数字。与其继续使用 StackOverflow 并要求其他人使用他们的神奇视觉来猜测可能出现的问题,不如实际检查是否有任何问题!这将是更大问题的一部分:“为什么这些基准测试的性能彼此不同?” 一旦你自学对每个基准都提出这个问题,你就会接受基准之道:你应该为解释而不是数字进行基准测试。

要回答这些问题,您可以尝试:

  • 建立可信的模型,计算机如何工作,以及你的代码如何与计算机交互——这通常需要调整基准参数,看看它是否以你当前模型预测的方式响应(看看自然科学如何处理本节中的自然看待)。

  • 查看计算机操作内部,看看它在忙什么。使用分析器,卢克!现在,对于您在测试中进行的纳米级基准测试,并非所有分析器都很好。您可能不得不开始使用生成的程序集和/或硬件计数器。环顾四周。JMH 附带一个非常基本的-prof perfasm.

换句话说,探索!

于 2014-08-26T15:51:39.520 回答