我正在编写一个代码,我在对数组值进行一些计算并将结果存储回数组。演示代码如下 -
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,但它没有帮助。
答案 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循环中,避免调用方法的开销。可以为您内联说明,但您不应该依赖它。