【发布时间】:2011-02-15 20:39:43
【问题描述】:
我很惊讶在 Java 源代码中看到 System.arraycopy 是本机方法。
当然是因为它更快。但是代码能够使用哪些原生技巧使其更快?
为什么不直接遍历原始数组并将每个指针复制到新数组 - 这肯定不是那么慢和麻烦?
【问题讨论】:
我很惊讶在 Java 源代码中看到 System.arraycopy 是本机方法。
当然是因为它更快。但是代码能够使用哪些原生技巧使其更快?
为什么不直接遍历原始数组并将每个指针复制到新数组 - 这肯定不是那么慢和麻烦?
【问题讨论】:
在本机代码中,可以使用单个 memcpy / memmove 来完成,而不是 n 不同的复制操作。性能差异很大。
【讨论】:
arraycopy的部分子case可以使用memcpy/memmove实现。其他需要对复制的每个元素进行运行时类型检查。
String 对象的Object[] 复制到String[]。见java.sun.com/javase/6/docs/api/java/lang/…的最后一段
memcpy 和 memmove 都是 O(n),但是 f.e. simd 优化它们few times 更快,所以你可能会说它们是 O(n/x),其中 x 取决于这些函数中使用的优化
它不能用 Java 编写。本机代码能够忽略或忽略 Object 数组和基元数组之间的区别。 Java 做不到,至少效率不高。
而且它不能用单个memcpy() 编写,因为重叠数组需要语义。
【讨论】:
memmove。虽然我认为这在这个问题的背景下没有太大区别。
有几个原因:
JIT 不太可能像手动编写的 C 代码那样生成高效的低级代码。使用低级 C 可以实现许多对于通用 JIT 编译器几乎不可能实现的优化。
手写C实现的一些技巧和速度比较见这个链接(memcpy,但原理是一样的):查看这个Optimizing Memcpy improves speed
C 版本几乎与数组成员的类型和大小无关。在 java 中不可能这样做,因为无法将数组内容作为原始内存块(例如指针)获取。
【讨论】:
当然,这取决于实现。
HotSpot 会将其视为“内在”并在调用站点插入代码。那是机器代码,而不是缓慢的旧 C 代码。这也意味着方法签名的问题在很大程度上消失了。
一个简单的复制循环非常简单,可以对其应用明显的优化。例如循环展开。究竟会发生什么再次取决于实现。
【讨论】:
在我自己的测试中,用于复制多维数组的 System.arraycopy() 比交错 for 循环快 10 到 20 倍:
float[][] foo = mLoadMillionsOfPoints(); // result is a float[1200000][9]
float[][] fooCpy = new float[foo.length][foo[0].length];
long lTime = System.currentTimeMillis();
System.arraycopy(foo, 0, fooCpy, 0, foo.length);
System.out.println("native duration: " + (System.currentTimeMillis() - lTime) + " ms");
lTime = System.currentTimeMillis();
for (int i = 0; i < foo.length; i++)
{
for (int j = 0; j < foo[0].length; j++)
{
fooCpy[i][j] = foo[i][j];
}
}
System.out.println("System.arraycopy() duration: " + (System.currentTimeMillis() - lTime) + " ms");
for (int i = 0; i < foo.length; i++)
{
for (int j = 0; j < foo[0].length; j++)
{
if (fooCpy[i][j] != foo[i][j])
{
System.err.println("ERROR at " + i + ", " + j);
}
}
}
打印出来:
System.arraycopy() duration: 1 ms
loop duration: 16 ms
【讨论】:
System.arraycopy 执行浅拷贝(仅复制对内部 float[]s 的 references),而嵌套的 for-loops 执行深层拷贝(float by float) .对fooCpy[i][j] 的更改将使用System.arraycopy 反映在foo 中,但不会使用嵌套的for 循环。