Después de una pregunta relacionada con la forma en que la JVM implementa la creación de cadenas basadas en char [], mencioné que no tiene lugar ninguna iteración cuando el char [] se copia al interior de la nueva cadena , ya que eventualmente se llama a System.arraycopy, que copia la memoria deseada utilizando una función como memcpy en un nivel nativo dependiente de la implementación (the original question).Implementación OpenJDK de System.arraycopy
Quería comprobarlo por mí mismo, así que descargué el código fuente de Openjdk 7 y comencé a explorarlo. me encontré con la implementación de System.arraycopy en el código fuente del OpenJDK C++, en openjdx/hotspot/src/share/vm/oops/objArrayKlass.cpp
:
if (stype == bound || Klass::cast(stype)->is_subtype_of(bound)) {
// elements are guaranteed to be subtypes, so no check necessary
bs->write_ref_array_pre(dst, length);
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// slow case: need individual subtype checks
Si los elementos necesitan ninguna comprobación de tipo (que es el caso de, por ejemplo, primitivas matrices de tipos de datos), copiar: : se llama a conjoin_oops_atomic.
La función Copy::conjoint_oops_atomic
reside en 'copy.hpp':
// overloaded for UseCompressedOops
static void conjoint_oops_atomic(narrowOop* from, narrowOop* to, size_t count) {
assert(sizeof(narrowOop) == sizeof(jint), "this cast is wrong");
assert_params_ok(from, to, LogBytesPerInt);
pd_conjoint_jints_atomic((jint*)from, (jint*)to, count);
}
Estamos dependientes de la plataforma, ya que la operación de copia tiene una aplicación diferente, basado en OS/arquitectura. Iré con Windows como ejemplo. openjdk\hotspot\src\os_cpu\windows_x86\vm\copy_windows_x86.inline.hpp
:
static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) {
// Do better than this: inline memmove body NEEDS CLEANUP
if (from > to) {
while (count-- > 0) {
// Copy forwards
*to++ = *from++;
}
} else {
from += count - 1;
to += count - 1;
while (count-- > 0) {
// Copy backwards
*to-- = *from--;
}
}
}
Y ... para mi sorpresa, se itera a través de los elementos (los valores POO), copiarlos uno por uno (aparentemente). ¿Alguien puede explicar por qué se hace la copia, incluso a nivel nativo, al recorrer los elementos de la matriz?
¡Guau, gracias! Fue un poco confuso mirar a través de la implementación OpenJDK por primera vez, así que esperaba haber obtenido algo mal. : P Entonces, ¿cómo crees que se lleva a cabo esta optimización? Hice algunas pruebas, y System.arraycopy es dos veces más rápido copiando 10000 ints que una forma Java normal. En C++, una tarea similar es notablemente más rápida, aunque los resultados pueden verse afectados por varias optimizaciones del compilador. –
Una copia en C++ no tiene un recolector de basura ejecutándose en un hilo separado. Incluso si no genera basura, el recolector debe robar unos ciclos para verificar que no tiene trabajo que hacer. No estoy seguro de si el compilador está desenrollando el loop de arraycopy o si el hardware está captando previamente todo el bloque de la matriz en caché. De hecho, con la optimización de microcódigo, está más allá de mi conocimiento profundo. Es por eso que la creación de perfiles es tan importante, es la prueba que demuestra que la optimización valió la pena. –