为数组索引赋值和为java中的Variable赋值之间的性能差异

时间:2013-12-19 14:35:01

标签: java arrays

我正在编写一个代码,我在对数组值进行一些计算并将结果存储回数组。演示代码如下 -

public class Test {
    private int[] x = new int[100000000];
    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        Test t = new Test();
        long start = System.nanoTime();
        for(int i=0;i<100000000;i++) {
            t.testing(i);
        }
        System.out.println("time = " + (System.nanoTime() - start)/1000);
    }

    public void testing(int a) throws Exception {
        int b=1,c=0;
        if(b<c || b < 1) {
            throw new Exception("Invalid inputs");
        }
        int d= a>>b;
        int e = a & 0x0f;
        int f = x[d];
        int g = x[e];
        x[d] = f | g;
    }
 }

程序的主要逻辑在于

int d= a>>b;
int e = a & 0x0f;
x[d] = f | g;

当我测试这段代码时,耗时110毫秒。 但是,如果我将结果分配给变量

,而不是将结果分配回x [d]
int h = f | g;

只花了3毫秒。

我想将结果分配回Array,但它大大限制了性能。 这是一个时间关键的计划。

所以我想知道在Java中是否有任何替代数组的方法或其他方式我可以避免这种阻碍?

我在默认的sun JVM配置下测试了这段代码。

P.S。我尝试过UNSAFE API,但它没有帮助。

3 个答案:

答案 0 :(得分:0)

你想要注意的是JVM将代码优化为零,因为它没有做任何有用的事情。

在您的情况下,您在110毫秒内执行1亿次通话,或每次通话约1.1纳秒。给定一个内存到L1缓存访问需要4个时钟周期,这是非常快的。在您的测试中,您在3毫秒内获得了1亿,这表明每次调用需要0.03纳秒,或者大约是时钟周期的1/10。对我来说这听起来不太可能,我希望如果循环的长度加倍,它仍然需要3毫秒。即你需要计算检测和消除代码所需的时间。

您遇到的一个基本问题是您有一个400 MB大小的数组。这不适合L1,L2或L3缓存。相反,它可能会进入主存储器,这通常需要200个时钟周期。最好的选择是减小阵列的大小,使其至少适合您的L3缓存。你的L3缓存有多大?如果它是24 MB,请尝试将阵列减少到16 MB,您应该会看到性能提升。

答案 1 :(得分:0)

有许多事情可能会发生。首先,尝试连续多次运行程序的每个版本并对其进行平均。其次,在Java中分配数组是一个执行错误检查的方法调用(例如在必要时抛出ArrayIndexOutOfBoundsException)。这自然会比变量赋值慢一点。如果您有一段非常时间敏感的代码,请考虑使用JNI进行数值运算:http://docs.oracle.com/javase/6/docs/technotes/guides/jni/。这通常会使您的阵列逻辑更快。

答案 2 :(得分:-1)

那是因为h是一个局部变量并且是在堆栈上分配的,而数组存储在主内存中,这对写入来说要慢一些。

另请注意,如果这是一个高性能应用程序,则应将主逻辑放在for循环中,避免调用方法的开销。可以为您内联说明,但您不应该依赖它。