我在 java 中对一个长数组执行了一个简短的基准测试,结果非常奇怪。似乎带有随机写入的顺序读取比带有顺序写入的随机读取要快 - 一半的时间。有人知道为什么吗??
这里有两种方法,在顺序读取时随机写入一些longs的数组(使用-Xmx2G左右运行),在随机写入时顺序读取:
import java.util.Random;
public class Scratch {
static Random random = new Random();
static long[] arr = new long[100000000];
static void seqReadRandWrite() {
for(int i=0;i<arr.length;i++) {
int at = random.nextInt(arr.length);
arr[at] = arr[i];
}
}
static void seqWriteRandRead() {
for(int i=0;i<arr.length;i++) {
int at = random.nextInt(arr.length);
arr[i] = arr[at];
}
}
public static void main(String[] args) throws Exception {
seqWriteRandRead(); // warm up
long nanos = System.nanoTime();
seqReadRandWrite();
System.out.println("Time: " + (System.nanoTime()-nanos) + "ns");
nanos = System.nanoTime();
seqWriteRandRead();
System.out.println("Time: " + (System.nanoTime()-nanos) + "ns");
}
}
我笔记本上的结果是
时间:2774662168ns
时间:6059499068ns
这意味着随机写入的速度是读取速度的两倍……还是?我的笔记本坏了吗?
ps.:这并不声称是一个基准,尽管有关基准的链接建议中的大部分要点都已涵盖。即使我多次运行已经 200,000,000 次操作,结果仍然保持不变。似乎(似乎!)将内存从随机位置移动到顺序 block 比将内存从顺序位置移动到随机 block 要慢,至少在这种大小的内存和上述执行方式的情况下。我想知道为什么?
最佳答案
您的基准测试产生的数字未能通过“它们有意义吗?”测试。在这种情况下,您应该始终双重/三重/四重检查您的方法......在将数字视为现实的真实反射(reflect)之前。
编写可靠的基准测试很难。对于 Java,尤其是很难,因为 Java 平台的某些方面可能会在您的基准测量中引入系统性失真......除非您特别允许/补偿它们。
但“检查您的方法”规则适用于所有实验……尤其是那些产生似乎没有意义的结果的实验。 (就像中微子比光速更快......)
要注意的另一件事是,一旦您重写了基准以考虑混杂因素,您可能仍然看到意想不到的数字。这里的问题是,像这样的基准测试的性能可能对 L1 和 L2 缓存的大小、缓存行的大小、不同内存级别的相对速度……以及它们与基准在紧密循环中产生的指令。
这些事情很复杂,难以分析,并且会产生违反直觉的行为。 (对我来说)不同的机器给出不同的测量性能也就不足为奇了。
因此,即使这些数字是真实的,从该基准测试中得出关于读写速度的任何一般性结论仍然是不安全的。即使您将它们限制在您的笔记本电脑上也不会。
关于Java VM 内存性能 - 数组写入比数组读取快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14636004/