3

我对自动装箱和性能很好奇,因为我在我的应用程序中做了很多对速度敏感的数学运算,所以我进行了一些测试......

public static void main(String[] args) {
    // Some initialization so I know it's not involved
    ArrayList<Integer> list = new ArrayList<Integer>();
    list.add(0);
    int[] regArray = new int[1];
    long total = 0;

    // This one uses an array and primitive type
    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        regArray[0] = i + 10;
        if (regArray[0] % 1000 == 0) total += regArray[0];
    }
    System.out.println("Runtime in millis: " + (System.currentTimeMillis() - start));
    System.out.println(total);

    // This one autoboxes, but still uses the Object type because it's a list
    total = 0;
    start = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        list.set(0, i + 10);
        if (list.get(0) % 1000 == 0) total += list.get(0);
    }
    System.out.println("Runtime in millis: " + (System.currentTimeMillis() - start));
    System.out.println(total);

    // This one doesn't autobox
    total = 0;
    start = System.currentTimeMillis();
    for (int i = 0; i < 10000000; i++) {
        list.set(0, new Integer(i + 10));
        if (list.get(0).intValue() % 1000 == 0) total += list.get(0).intValue();
    }
    System.out.println("Runtime in millis: " + (System.currentTimeMillis() - start));
    System.out.println(total);
}

这是一个示例输出:

Runtime in millis: 78
50005000000
Runtime in millis: 250
50005000000
Runtime in millis: 250
50005000000

这似乎表明我应该远离List<>数学,速度敏感的应用程序和子类。你同意吗,stackoverflow?

编辑:我的实际用例是我需要存储几百个ints 和floats ,它们会经常且很大程度上不可预测地变化(我说主要是因为它们会保持在一个狭窄的范围内,但我不知道他们会在那个范围内做什么范围窄),我需要对这些数字进行数学运算时需要毫秒级的响应时间。

4

3 回答 3

3

微基准测试很难!我重写了您的基准以使用caliper

import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;

import java.util.ArrayList;

public class ListsBenchmark extends SimpleBenchmark {

    private final ArrayList<Integer> list = new ArrayList<Integer>();
    int[] regArray = new int[1];
    long total;

    @Override
    protected void setUp() throws Exception {
        list.add(0);
        total = 0;
    }

    public long timeArrayAndPrimitiveType(int reps) {
        for (int i = 0; i < reps; i++) {
            regArray[0] = i + 10;
            if (regArray[0] % 1000 == 0)
                total += regArray[0];
        }
        return total;
    }

    public long timeListWithAutoboxing(int reps) {
        for (int i = 0; i < reps; i++) {
            list.set(0, i + 10);
            if (list.get(0) % 1000 == 0)
                total += list.get(0);
        }
        return total;
    }

    public long timeNoAutoboxing(int reps) {
        for (int i = 0; i < reps; i++) {
            list.set(0, new Integer(i + 10));
            if (list.get(0).intValue() % 1000 == 0)
                total += list.get(0).intValue();
        }
        return total;
    }

    public static void main(String[] args) {
        Runner.main(ListsBenchmark.class, new String[]{});
    }

}

我没有修改你的原始代码。我发现的是:

  • 阵列快 3 倍左右
  • 创建新Integer的稍微快一点(!),也许缓存有一些价格,或者它只是我的架构(32 位 Ubuntu,4 核和 3 GiB 内存笔记本电脑)

在图表上(随意运行它!):

卡尺

于 2012-11-19T21:55:17.083 回答
2

如果您有预定数量的整数,那么将它们存储在一个数组中(假设它们需要存储并且不能流式传输!)通常比 a 快java.util.ArrayList,是的。

但是,在许多情况下,您可能有不同的数据大小,因此动态可调整大小的集合变得非常有用 - 替代方法通常是编写您自己的实现ArrayList

幸运的是,有许多第三方库基于原始类型(int 等)而不是对象(Integer 等)实现 List。你可以看看这些。

于 2012-11-19T21:48:14.320 回答
1

编写基准是一项艰巨的任务,而您的基准也不是一个好的基准,至少有以下原因:

  • 在 main 方法中做所有事情,而不让 Hotspot 进来,JIT 编译你的代码会导致结果错误
  • 使用new Integer()而不是Integer.valueOf()不允许您使用整数缓存
  • 值和操作是不现实的。大多数时候,值接近 0,而不是在 10,000,000 范围内

基元和数组通常比对象和集合更快,但是如果不测量您的实际代码,在现实条件下,很难判断使用基元和数组获得的收益是显着还是完全可以忽略不计。大多数情况下,IO 操作会损失性能。

于 2012-11-19T21:39:01.997 回答