性能测试中的第一个循环较慢

First loop slower in performance test

我想亲眼看看某些数据结构的行为方式。我从 ArrayList 开始,并用自定义 class 的对象填充它。但是当我玩弄它时,我注意到 运行ning 测试方法 X 在上面它的执行速度要慢得多,或者如果我第二次做同样的工作两次,速度最多快 6 倍。

这里有一些例子:

private void generateItems(int amount)
{
    System.out.println("Populating list with " + amount + " items...");
    long time = System.nanoTime();
    for (int i = 0; i < amount; i++)
    {
        items.add(new Item("Type", "Subtype", random.nextInt(width), random.nextInt(height)));
    }
    System.out.println(timePassed(time) + " List size: " + items.size());
}

private void sortList(int x, int y)
{
    System.out.println("Sorting list...");
    long time = System.nanoTime();
    Collections.sort(items, new ItemComparator(x, y));
    System.out.println(timePassed(time) + " List sorted.");
    System.out.println("First: " + items.get(0));
}

现在让我们运行这两个方法两次:

    items = new ArrayList<>();
    generateItems(100000); //33ms
    sortList(0, 0); //118ms

    items = new ArrayList<Item>();
    generateItems(100000); //5ms
    sortList(0, 0); //28ms

我知道当我对一个已经排序的列表进行排序时会花费更少的时间,因为计算机会更好地赌博结果,但在这里我生成了两个完全随机的列表。

我还有一些涉及迭代和条件项检索的方法,它们的行为都相同:第一个比 运行 以后要慢得多。

为了继续对其他数据结构进行测试,我想了解更多关于此行为的信息。也许我在这里做错了什么或者结果是预期的,为什么?我如何进行这些比较可靠的测试?

Perhaps I'm doing something wrong here or is the outcome expected and why?

是的,Java 是默认带有 JIT 的 JVM。这意味着代码是动态编译的,因为它是 运行。因此,开始时 运行 速度较慢,但​​随着它获得有关程序如何运行的更好采样数据而加快速度 运行。

您可以在命令行中使用-XX:+PrintCompilation查看一些细节。注意一些方法会被多次编译,有时编译优化成本更高,有时因为它有更好的数据来优化代码。

How do I make somewhat reliable tests like these?

简单的答案是 运行 重复测试并至少忽略基准测试的前 2 秒。

要获得更好的答案,请使用 JMH。默认情况下,这将 运行 每个测试 10 秒,超过 20 次,并在 运行 进行实际测试和编写微基准测试的许多更好方法之前忽略此预热。

How do I make somewhat reliable tests like these?

不要使用手动基准测试,使用 OpenJdk/jmh:

class MyTest {
@Benchmark
private void generateItemsTest() {
   generateItems(100000);
}

 @Benchmark
private void sortListTest() {       
   generateItems(100000);
   sort(0, 0)
}

public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(MyTest.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();
    }

在 Maven 的 pom.xml 中你应该添加

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.11.2</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.11.2</version>
</dependency>

或以其他方式在您的项目中安装 JMH