Java独立变量与数组性能比较

9

我正在尝试使用Java,并想知道以下内容在性能方面有何不同。我知道过早地优化会影响编程,但我的好奇心只是为了以后的参考。

public class Type1{
     int[] data = new data[4];
     public int getData( int index ){
          return data[index];
     }
}

public class Type2{
     int data1;
     int data2;
     int data3;
     int data4;
     public int getData1(){
          return data1;
    }
    public int getData2(){
          return data2;
    }
    public int getData3(){
          return data3;
    }
    public int getData4(){
          return data4;
    }
}

可以理解的是,有许多因素可能会产生差异,但一定会在某些方面表现出明显的性能。显然,从设计上看,Type1类是一个更具吸引力的解决方案,但似乎在检索数据时它多了一步操作,即进入数组以获取int,而Type2则直接获取数据。也许如果它们持有Class对象数组,则差异会更加明显,因为Java可能会保证使用数组成员的引用。我完全错了吗?


我非常确定任何差异都是微不足道的。 - DonyorM
你知道是否有一种方法可以反编译Java代码以比较其汇编代码吗? - sgtHale
5
当然,第二种方式不会执行一些额外的数组步骤(检查索引等),但您应该记住,使用第二种方式将失去数组的好处(并且代码可维护性更差!) - Marco Acierno
@sgtHale 那可能应该是一个单独的问题,而且它已经存在于SO上了,但这个链接似乎很有用:http://blog.leocad.io/how-to-decompile-dalvik-and-java-code/ - DonyorM
2个回答

5

与API使用的差异相比,两种方法之间的运行时速度差异肯定微不足道。

但是,如果我们重新安排事物,使两者的API相同,那么我们发现运行时速度差异确实可以忽略不计。以下代码计时两种方法,我得到了大约13秒的结果。您的结果可能会有所不同。

不过,查看字节码是否优化了很多差异可能是值得的。

public class Program {
    public static interface Type {
        int getData1();

        int getData2();

        int getData3();

        int getData4();
    }

    public static class Type1 implements Type {
        private int[] data;

        public Type1(int data1, int data2, int data3, int data4) {
            data = new int[] { data1, data2, data3, data4 };
        }

        @Override
        public int getData1() {
            return data[0];
        }

        @Override
        public int getData2() {
            return data[1];
        }

        @Override
        public int getData3() {
            return data[2];
        }

        @Override
        public int getData4() {
            return data[3];
        }
    }

    public static class Type2 implements Type {
        private int data1;
        private int data2;
        private int data3;
        private int data4;

        public Type2(int data1, int data2, int data3, int data4) {
            this.data1 = data1;
            this.data2 = data2;
            this.data3 = data3;
            this.data4 = data4;
        }

        @Override
        public int getData1() {
            return data1;
        }

        @Override
        public int getData2() {
            return data2;
        }

        @Override
        public int getData3() {
            return data3;
        }

        @Override
        public int getData4() {
            return data4;
        }
    }

    public static void main(String[] args) {
        timeType(new Type1(1, 2, 3, 4));
        timeType(new Type2(1, 2, 3, 4));
    }

    private static void timeType(Type type) {
        long start = System.currentTimeMillis();
        int total = 0;

        for (long i = 0; i < 10000000000l; i++) {
            total += type.getData1();
            total += type.getData2();
            total += type.getData3();
            total += type.getData4();
        }

        System.out.println(total);
        System.out.println(System.currentTimeMillis() - start);
    }
}

那肯定是微不足道的。感谢您的演示。 - sgtHale
我认为访问方式基本相同,除了在数组的情况下可能会有一点指针算术(数组指针基址加索引)。但是可能存在一种汇编指令可以实现基址加索引的操作。 - Joffrey
1
我建议编写一个JMH测试来比较这两种设计的性能。此外,如果您处理大量实例,则性能可能会有所不同。 - Emmanuel Bourg

2
不,你不是。你的问题非常类似于JGit实现中的问题:

JGit在表示SHA-1时没有有效的方法。 C可以说“unsigned char[20]”,并将其内联到容器的内存分配中。 Java中的byte[20]将额外消耗16个字节的内存,并且访问速度较慢, 因为字节本身位于与容器对象不同的内存区域中。 我们尝试通过从byte[20]转换为5个int来解决它,但这会消耗我们的机器指令。

来源于http://marc.info/?l=git&m=124111702609723&w=2 请参见ObjectId implementation
请注意,您可以提供Type1的API给Type2,而无需(可能很昂贵的)索引切换。
public class Type2 {
    private static final Unsafe U;
    private static final long data1Offset;
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            U = (Unsafe) f.get(null);
            Field data1Field = Type2.class.getDeclaredField("data1");
            data1Offset = U.objectFieldOffset(data1Field);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    int data1;
    int data2;
    int data3;
    int data4;
    public int getData(int index) {
         assert 0 <= index && index < 4 : "index out of range!";
         return U.getInt(this, data1Offset + (index << 2));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接