好奇与求知
学无止境,在计算机行业尤其如此。好奇和求知可以使我们的专业技能在广度和深度两个维度得到扩展。举个例子,我们实现将数组元素拷贝到另外一个数组中,这里大家可能会选择自己写一个循环来实现,而在java
里有个内置函数叫System.arrayCopy()
,网上有不少实验证明了后者比前者要快。为什么会快呢?看一下源代码:
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
复制代码
内部调用了本地方法,那调用本地方法就一定比自己写的循环快吗?我们看一下这个本地方法
JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
jobject dst, jint dst_pos, jint length))
JVMWrapper("JVM_ArrayCopy");
// Check if we have null pointers
if (src == NULL || dst == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
arrayOop s = arrayOop(JNIHandles::resolve_non_null(src));
arrayOop d = arrayOop(JNIHandles::resolve_non_null(dst));
assert(s->is_oop(), "JVM_ArrayCopy: src not an oop");
assert(d->is_oop(), "JVM_ArrayCopy: dst not an oop");
// Do copy
Klass::cast(s->klass())->copy_array(s, src_pos, d, dst_pos, length, thread);
JVM_END
复制代码
真正的拷贝动作在最后一行,它对应的代码
void typeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS) {
assert(s->is_typeArray(), "must be type array");
// Check destination
if (!d->is_typeArray() || element_type() != typeArrayKlass::cast(d->klass())->element_type()) {
THROW(vmSymbols::java_lang_ArrayStoreException());
}
// Check is all offsets and lengths are non negative
if (src_pos < 0 || dst_pos < 0 || length < 0) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
// Check if the ranges are valid
if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
|| (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
// Check zero copy
if (length == 0)
return;
// This is an attempt to make the copy_array fast.
int l2es = log2_element_size();
int ihs = array_header_in_bytes() / wordSize;
char* src = (char*) ((oop*)s + ihs) + ((size_t)src_pos << l2es);
char* dst = (char*) ((oop*)d + ihs) + ((size_t)dst_pos << l2es);
Copy::conjoint_memory_atomic(src, dst, (size_t)length << l2es);//还是在这里处理copy
}
复制代码
这个方法前面是各种判断,真正的拷贝动作也是在最后一行,然后再逐层往下找最后找到如下方法
void _Copy_conjoint_jints_atomic(jint* from, jint* to, size_t count) {
if (from > to) {
jint *end = from + count;
while (from < end)
*(to++) = *(from++);
}
else if (from < to) {
jint *end = from;
from += count - 1;
to += count - 1;
while (from >= end)
*(to--) = *(from--);
}
}
复制代码
在这里我看到它的输入参数包含两个数组的首地址,然后在此基础上逐项复制。详细中间过程的C++
代码可以参考这个链接:www.cnblogs.com/yakovchang/…
看到这里可能还是有点懵,这不依然是逐项拷贝吗?的确是,但是这里省去了每次循环时重新定位两个数组位置的问题(数组寻址),我们手写的循环每次都要重新定位两个数组的首地址,然后逐项拷贝,而上述方法已经把两个数组首地址传递了过来,不用每次寻址,直接内存拷贝即可。
上面举的是一个技术相关的例子,但其实也可以扩展到其他方面,比如我们去用户现场实际观察了解一下,看看用户是如何使用的,很可能跟自己想象中的不太一样。像我现在的公司,内部有各种技术,业务以及其他形式的的知识分享,我们都可以选择自己感兴趣的去听,这就大大拓宽了我们的知识面。
总结
不管是那个行业,都是向着专业化,多元化的方向发展,我们必须保持一个好奇和求知的心态才能不断扩展我们的知识广度和深度,从而适应行业的发展。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END