gpt4 book ai didi

java - 为什么执行 Mockito 模拟的性能如此不稳定?

转载 作者:塔克拉玛干 更新时间:2023-11-02 07:46:29 24 4
gpt4 key购买 nike

对于为什么执行 Mockito 模拟所花费的时间如此不稳定,谁能给出解释,甚至更好的建议修复?下面是我能想到的最简单的 SSCCE:

import static org.mockito.Mockito.mock;

public class TestSimpleMockTiming
{
public static final void main (final String args [])
{
final Runnable theMock = mock (Runnable.class);

int tookShort = 0;
int tookMedium = 0;
int tookLong = 0;
int tookRidiculouslyLong = 0;
long longest = 0;

for (int n = 0; n < 2000000; n++)
{
final long startTime = System.nanoTime ();
theMock.run ();
final long duration = System.nanoTime () - startTime;

if (duration < 1000000) // 0.001 seconds
tookShort++;
else if (duration < 100000000) // 0.1 seconds
tookMedium++;
else if (duration < 1000000000) // 1 second !!!
tookLong++;
else
tookRidiculouslyLong++;

longest = Math.max (longest, duration);
}

System.out.println (tookShort + ", " + tookMedium + ", " + tookLong + ", " + tookRidiculouslyLong);
System.out.println ("Longest duration was " + longest + " ns");
}
}

如果我运行它(从 Eclipse 中,在 Win 7 x64 上使用 JDK 1.7.45),典型的输出如下:

1999983, 4, 9, 4
Longest duration was 5227445252 ns

因此,虽然在大多数情况下 mock 的执行速度非常快,但有几个执行时间甚至超过 1 秒。对于什么都不做的方法来说,这是永恒的。从我的实验来看,我不认为问题是 System.nanoTime () 的准确性,我认为模拟确实需要那么长时间才能执行。我可以做些什么来改进这一点并使计时行为更加一致吗?

(仅供引用,为什么这是一个问题是因为我有一个包含各种框架的 Swing 应用程序,我尝试为框架编写 JUnit 测试以便我可以测试布局管理器是否正确运行而不必启动整个应用程序并导航到正确的屏幕。在一个这样的测试中,屏幕使用 javax.swing.Timer 来实现滚动,因此当鼠标保持在帧末尾附近时,显示器将在一个区域周围平移。我注意到了这种行为这是非常不稳定的,虽然通常很好的滚动会周期性地卡住一秒钟,看起来很可怕。我围绕这个写了一个 SSCCE,认为问题是不能依赖 Swing Timers 以一致的方式触发率,并在 SSCCE 中完美运行。

经过几个小时的努力,然后试图找出我的真实代码和滚动演示 SSCCE 之间的差异,我开始在重复运行的代码块周围放置纳米计时器,注意到我的 paintComponent 方法所花费的时间非常不稳定并最终将其缩小为模拟电话。从运行真实应用程序测试屏幕,滚动表现流畅,这只是 JUnit 测试的一个问题,因为模拟调用,这导致我单独测试一个简单的模拟与上面发布的 SSCCE。)

非常感谢!

最佳答案

此测试在多个方面存在缺陷。如果你想正确地进行基准测试,我强烈建议使用 JMH ,它是由 Alexey Shipilev 完成的,他比我们聪明得多,而且在 JVM 方面的知识绝对比我们心爱的星球上大多数使用 Java 的人都多。

这是该测试最明显的缺陷。

  1. 测试忽略了 JVM 正在做什么,比如预热阶段、编译 C1 和 C2 线程、GC、线程问题(即使这段代码不是多线程的,JVM/OS 可能必须做一些事情否则)等...

  2. 测试似乎确实忽略了实际操作系统/JVM/CPU 组合是否提供高达纳秒的正确分辨率。

    即使有 System.nanoTime(),您是否确定 JVM 和操作系统具有正确的分辨率。例如,在 Windows 上,JVM 无法访问真正的纳秒,而是访问某个计数器,而不是挂钟时间。 javadoc声明这一点,这里是片段:

    This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary origin time (perhaps in the future, so values may be negative). The same origin is used by all invocations of this method in an instance of a Java virtual machine; other virtual machine instances are likely to use a different origin.

    This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().

  3. 测试也忽略了 Mockito 的工作原理。

    Mockito 将每个调用存储在它自己的模型中,以便能够在执行场景之后验证这些调用。因此,在循环的每次迭代中,Mockito 都会存储另一个调用,最多 2M 个调用,这将影响 JVM(也许模拟实例将保留几代并提升到终身,这对 GC 来说肯定成本更高)。这意味着迭代次数越多,这段代码对 JVM 而不是 Mockito 的压力就越大。

    我相信它没有发布(但是在 jcentral 上有开发二进制文件),但是 Mockito 将提供一个设置以允许 mockito 仅 stub 因此它不会存储调用,这可能允许 Mockito 很好地适应这样的场景。

  4. 测试缺乏适当的统计分析。

    有趣的是,测试代码采用了伪百分位数方法。哪个好!虽然它不是那样工作的,但在这种情况下,它无法解决大问题。相反,它应该记录每一个度量,以便提取 mockito 随着迭代计数的增加所花费的时间的演变趋势。

    如果您愿意,最好存储每个记录的测量值,这样就可以将它们提供给适当的统计分析工具,例如 R,以便提取图表、百分位数据等.

    关于统计问题,使用 HDRHistogram 肯定会很有趣.当然在微基准测试之外,因为它会影响内存并改变微基准测试的结果。让我们为 JMH 保留它。

如果您更改代码以使用 JMH,则第 1 点和第 2 点都可以解决。

希望对您有所帮助。

关于java - 为什么执行 Mockito 模拟的性能如此不稳定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24983577/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com