【问题标题】:Java independent variable vs array performanceJava自变量与数组性能
【发布时间】:2014-04-26 00:29:06
【问题描述】:

我正在玩 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 使用数组成员的引用。我是不是完全没球了?

【问题讨论】:

  • 我很确定任何差异都可以忽略不计。
  • 不知道有没有办法反编译java代码来比较它的Assembly?
  • 当然第二个不会做一些额外的数组步骤(检查索引等),但你应该记住,使用第二种样式你将失去数组的好处(以及可维护性较差的代码!)
  • @sgtHale 这可能应该是一个单独的问题,它已经存在于 SO,但这个链接似乎很有用:blog.leocad.io/how-to-decompile-dalvik-and-java-code

标签: java arrays optimization


【解决方案1】:

与 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);
    }
}

【讨论】:

  • 那肯定可以忽略不计。感谢您的演示。
  • 我认为访问几乎是相同的,除了在数组的情况下非常少的指针算术(数组指针基数的总和 + 索引)。但是可能存在一个汇编指令,无论如何都会执行 base+index。
  • 我建议编写一个 JMH 测试来比较两种设计的性能。此外,如果您处理大量实例,性能可能会有所不同。
【解决方案2】:

不,你不是。您的问题与JGit 实现中的问题非常相似:

JGit 苦于没有一种有效的方式来表示 SHA-1。 C 可以只说“unsigned char[20]”并将其内联到 容器的内存分配。 Java 中的 byte[20] 将花费 额外 16 字节内存,访问速度较慢,因为 字节本身与 容器对象。我们尝试通过从 byte[20] 到 5 个整数,但这需要我们使用机器指令。

来自http://marc.info/?l=git&m=124111702609723&w=2

ObjectId implementation

另请注意,您可以为Type2 提供Type1 的API,而无需(可能很昂贵)索引开关:

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));
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-04
    • 2014-03-26
    • 2015-05-20
    • 2012-09-17
    • 2016-01-05
    • 1970-01-01
    相关资源
    最近更新 更多