为什么System.arraycopy是Java本机的?
我很惊讶地在Java源代码中看到System.arraycopy是一个本机方法 当然原因是因为它更快。但是,代码能够采用哪些本机技巧使其更快为什么System.arraycopy是Java本机的?,java,native,arrays,Java,Native,Arrays,我很惊讶地在Java源代码中看到System.arraycopy是一个本机方法 当然原因是因为它更快。但是,代码能够采用哪些本机技巧使其更快 为什么不直接在原始数组上循环并将每个指针复制到新数组上呢?当然这并不是那么慢和麻烦吗?在本机代码中,可以使用单个memcpy/,而不是n个不同的复制操作。性能上的差异很大。它不能用Java编写。本机代码能够忽略或消除对象数组和原语数组之间的差异。Java无法做到这一点,至少效率不高 由于重叠数组需要语义,因此不能使用单个memcpy()来编写它。有几个原因
为什么不直接在原始数组上循环并将每个指针复制到新数组上呢?当然这并不是那么慢和麻烦吗?在本机代码中,可以使用单个
memcpy
/,而不是n个不同的复制操作。性能上的差异很大。它不能用Java编写。本机代码能够忽略或消除对象数组和原语数组之间的差异。Java无法做到这一点,至少效率不高
由于重叠数组需要语义,因此不能使用单个
memcpy()
来编写它。有几个原因:
当然,它取决于实现 HotSpot会将其视为“内在的”,并在调用站点插入代码。这是机器代码,不是很慢的旧C代码。这也意味着方法签名的问题在很大程度上消失了
简单的复制循环足够简单,可以对其应用明显的优化。例如,循环展开。确切地说,发生的情况同样取决于实现。在我自己的测试系统中。用于复制多维数组的arraycopy()比用于循环的交错快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);
}
}
}
好吧,那么
memmove
。虽然我不认为这在这个问题的上下文中有多大区别。也不是memmove(),请参阅@Stephen C对另一个答案的评论。已经看到了,因为这恰好是我自己的答案;-)不过还是要谢谢你。@Geek数组重叠了。如果源数组和目标数组相同,并且只有偏移量不同,则会仔细指定行为,并且memcpy()不符合要求。它不能用Java编写吗?难道一个人不能编写一个泛型方法来处理对象的子类,然后为每个基本类型编写一个泛型方法吗?Java代码可以得到优化。事实上,实际发生的是生成了比C更高效的机器代码。我同意,有时JIT代码会更好地进行局部优化,因为它知道在哪个处理器上运行。然而,由于它是“及时的”,它将永远无法使用所有需要更长时间才能执行的非本地优化。此外,它将永远无法匹配手工编制的C代码(这也可能会考虑处理器,并通过为特定处理器编译或某种运行时检查部分否定JIT优势)。我认为Sun JIT编译器团队会对其中的许多观点提出质疑。例如,我相信HotSpot会进行全局优化,以消除不必要的方法调度,JIT没有理由不能生成特定于处理器的代码。还有一点是,JIT编译器可以根据当前应用程序运行的执行行为进行分支优化。@Stephen C-关于分支优化,aldough您还可以使用C/C++编译器执行静态性能评测,以达到类似的效果。我还认为hotspot有两种操作模式——桌面应用程序不会使用所有可用的优化来实现合理的启动时间,而服务器应用程序将得到更积极的优化。总而言之,您获得了一些优势,但也失去了一些。System.arrayCopy不是用C实现的,这使得这个答案无效。实际上,只有arrayCopy
的一些子类可以用memcpy
/memmove
实现。其他需要为每个元素复制一个运行时类型检查。@史蒂芬C,有趣的是,为什么?@ p t t o or k -考虑从<代码>对象[][COD] >用<代码>字符串< /COD>对象填充到<代码>字符串[]/COD>。参见Peter的最后一段,Object[]和byte[]+char[]是最常被复制的,它们都不需要显式的类型检查。编译器足够聪明,除非需要,否则不会进行检查,事实上99.9%的情况下,它不是。有趣的是,小尺寸的拷贝(小于缓存线)占主导地位,所以“memcpy”对于小尺寸的东西来说速度非常重要。@jainilvachhanimemcpy
和memmove
都是O(n),但是由于f.e.simd优化,它们的速度快了几倍,所以你可以说它们是O(n/x),其中x依赖于这些函数中使用的优化这是一个非常好的答案:),特别是提到内部函数。没有他们,简单的迭代可能会更快,因为不管怎么说,JIT通常都会展开它,即使这个问题很老,只是为了记录:这不是一个公平的基准(更不用说这样的基准一开始是否有意义了)System.arraycopy执行浅复制(仅复制对内部float[]
s的引用),而嵌套的for
-循环执行深复制(float
byfloat
)。对fooCpy[i][j]
的更改将通过System.arraycopy
反映在foo
中,但不会对-循环使用嵌套的。
System.arraycopy() duration: 1 ms
loop duration: 16 ms