java - 在 Java : Is my benchmarking code wrong? 中对小型数组与列表进行基准测试

标签 java performance microbenchmark

Disclaimer: I have looked through this question and this question but they both got derailed by small details and general optimization-is-unnecessary concerns. I really need all the performance I can get in my current app, which is receiving-processing-spewing MIDI data in realtime. Also it needs to scale up as well as possible.

我正在比较array小列表的大量读取性能到 ArrayList并且还只是掌握了变量。我发现数组胜过 ArrayList 2.5 倍,甚至胜过仅拥有对象引用。

我想知道的是:

  1. 我的基准测试还好吗? 我已经改变了测试顺序和运行次数,没有任何变化。我也使用毫秒而不是纳秒来无济于事。
  2. 我是否应该指定任何 Java 选项来最小化这种差异?
  3. 如果这种差异是真实的,在这种情况下我不应该更喜欢Test[]吗?至 ArrayList<Test>在这种情况下并放入转换它们所需的代码?显然我读的比写的多得多。

JVM 是 OSX 上的 Java 1.6.0_17,它肯定是在 Hotspot 模式下运行。

  public class ArraysVsLists {

    static int RUNS = 100000;

    public static void main(String[] args) {
        long t1;
        long t2;

        Test test1 = new Test();
        test1.thing = (int)Math.round(100*Math.random());
        Test test2 = new Test();
        test2.thing = (int)Math.round(100*Math.random());

        t1 = System.nanoTime();

        for (int i=0; i<RUNS; i++) {
            test1.changeThing(i);
            test2.changeThing(i);
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1) + " How long NO collection");

        ArrayList<Test> list = new ArrayList<Test>(1);
        list.add(test1);
        list.add(test2);
        // tried this too: helps a tiny tiny bit 
        list.trimToSize();

        t1= System.nanoTime();

        for (int i=0; i<RUNS; i++) {
            for (Test eachTest : list) {
                eachTest.changeThing(i);
            }
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1) + " How long collection");


        Test[] array = new Test[2];
        list.toArray(array);

        t1= System.nanoTime();

        for (int i=0; i<RUNS; i++) {
            for (Test test : array) {
                test.changeThing(i);
            }
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1) + " How long array ");

    }
}

class Test {
    int thing;
    int thing2;
    public void changeThing(int addThis) {
        thing2 = addThis + thing;
    }
}

最佳答案

微基准测试很难在像 Java 这样的平台上正确执行。您肯定必须将要进行基准测试的代码提取到单独的方法中,将它们作为预热运行几千次,然后进行测量。我已经这样做了(下面的代码),结果是通过引用直接访问的速度是通过数组的三倍,但集合仍然慢了 2 倍。

这些数字基于 JVM 选项 -server -XX:+DoEscapeAnalysis。如果没有 -server,使用集合的速度将急剧变慢(但奇怪的是,直接访问和数组访问要快很多,这表明发生了一些奇怪的事情)。 -XX:+DoEscapeAnalysis 为收集产生了另外 30% 的加速,但它是否适用于您的实际生产代码非常值得怀疑。

总的来说,我的结论是:忘记微基准测试吧,它们很容易产生误导。尽可能接近生产代码进行测量,而无需重写整个应用程序。

import java.util.ArrayList;

public class ArrayTest {

    static int RUNS_INNER = 1000;
    static int RUNS_WARMUP = 10000;
    static int RUNS_OUTER = 100000;

    public static void main(String[] args) {
        long t1;
        long t2;

        Test test1 = new Test();
        test1.thing = (int)Math.round(100*Math.random());
        Test test2 = new Test();
        test2.thing = (int)Math.round(100*Math.random());

        for(int i=0; i<RUNS_WARMUP; i++)
        {
            testRefs(test1, test2);            
        }
        t1 = System.nanoTime();
        for(int i=0; i<RUNS_OUTER; i++)
        {
            testRefs(test1, test2);            
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1)/1000000.0 + " How long NO collection");

        ArrayList<Test> list = new ArrayList<Test>(1);
        list.add(test1);
        list.add(test2);
        // tried this too: helps a tiny tiny bit 
        list.trimToSize();

        for(int i=0; i<RUNS_WARMUP; i++)
        {
            testColl(list);
        }
        t1= System.nanoTime();

        for(int i=0; i<RUNS_OUTER; i++)
        {
            testColl(list);
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1)/1000000.0 + " How long collection");


        Test[] array = new Test[2];
        list.toArray(array);

        for(int i=0; i<RUNS_WARMUP; i++)
        {
            testArr(array);            
        }
        t1= System.nanoTime();

        for(int i=0; i<RUNS_OUTER; i++)
        {
            testArr(array);
        }

        t2 = System.nanoTime();
        System.out.println((t2-t1)/1000000.0 + " How long array ");

    }

    private static void testArr(Test[] array)
    {
        for (int i=0; i<RUNS_INNER; i++) {
            for (Test test : array) {
                test.changeThing(i);
            }
        }
    }

    private static void testColl(ArrayList<Test> list)
    {
        for (int i=0; i<RUNS_INNER; i++) {
            for (Test eachTest : list) {
                eachTest.changeThing(i);
            }
        }
    }

    private static void testRefs(Test test1, Test test2)
    {
        for (int i=0; i<RUNS_INNER; i++) {
            test1.changeThing(i);
            test2.changeThing(i);
        }
    }
}

class Test {
    int thing;
    int thing2;
    public void changeThing(int addThis) {
        thing2 = addThis + thing;
    }
}

关于java - 在 Java : Is my benchmarking code wrong? 中对小型数组与列表进行基准测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2227947/

相关文章:

java - 创建一个与另一个与原始对象有关系的对象有什么含义?

mysql - 多变量where子句的最佳mysql索引

java - arraylist vs linked list。为什么最后添加时链表变慢?

java - 触发链接但不更改页面内容

Java ActionListener 问题

java - Android GridLayout 不显示

java - 避免具有不同行数的动态表行流向 XWPFDocument(Apache POI) 中的不同页面

java - IO 与数据库 : Which is faster?

java - 由于 JIT 编译,Caliper 基准测试失败

java - 如何用 Java 编写正确的微基准测试?