Estoy tratando de obtener el mayor rendimiento posible de algún método interno.
El código de Java es:
List<DirectoryTaxonomyWriter> writers = Lists.newArrayList();
private final int taxos = 4;
[...]
@Override
public int getParent(final int globalOrdinal) throws IOException {
final int bin = globalOrdinal % this.taxos;
final int ordinalInBin = globalOrdinal / this.taxos;
return this.writers.get(bin).getParent(ordinalInBin) * this.taxos + bin; //global parent
}
En mi generador de perfiles vi que hay un 1% de gasto de CPU java.util.Objects.requireNonNull
, pero ni siquiera lo llamo. Al inspeccionar el código de bytes, vi esto:
public getParent(I)I throws java/io/IOException
L0
LINENUMBER 70 L0
ILOAD 1
ALOAD 0
INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
POP
BIPUSH 8
IREM
ISTORE 2
Entonces el compilador genera esta verificación (¿inútil?). Trabajo en primitivas, que no pueden ser de null
todos modos, entonces, ¿por qué el compilador genera esta línea? ¿Es un error? ¿O comportamiento "normal"?
(Podría trabajar con una máscara de bits, pero tengo curiosidad)
[ACTUALIZAR]
El operador parece no tener nada que ver con eso (ver la respuesta a continuación)
Usando el compilador eclipse (versión 4.10) obtengo este resultado más razonable:
getParent público (I) lanzo java / io / IOException L0 LINENUMBER 77 L0 ILOAD 1 ICONST_4 IREM ISTORE 2 L1 LINENUMBER 78 L
Entonces eso es más lógico.
INVOKESTATIC
javac
no genera esto.openjdk version "11.0.6" 2020-01-14
en ubuntu 64 bit.Respuestas:
Por qué no?
Asumiendo
una llamada como
c.test()
dondec
se declara comoC
debe lanzar cuandoc
esnull
. Tu método es equivalente amientras trabajas solo con constantes. Al
test
ser no estático, la verificación debe hacerse. Normalmente, se haría implícitamente cuando se accede a un campo o se llama a un método no estático, pero no lo hace. Por lo tanto, se necesita una verificación explícita. Una posibilidad es llamarObjects.requireNonNull
.El bytecode
No olvides que el bytecode es básicamente irrelevante para el rendimiento. La tarea de
javac
es producir algún código de bytes cuya ejecución corresponda con su código fuente. No está destinado a hacer ninguna optimización, ya que el código optimizado suele ser más largo y difícil de analizar, mientras que el código de bytes es en realidad el código fuente para el compilador JIT de optimización. Porjavac
lo tanto, se espera que sea simple ...El desempeño
Primero culparía al perfilador. Perfilar Java es bastante difícil y nunca puedes esperar resultados perfectos.
Probablemente deberías intentar hacer que el método sea estático. Seguramente deberías leer este artículo sobre cheques nulos .
fuente
this
no es estáticanull
. Como usted mismo dijo, una llamada comoc.test()
debe fallar cuandoc
esnull
y tiene que fallar inmediatamente, en lugar de ingresar al método. Entoncestest()
, dentro ,this
nunca puede sernull
(de lo contrario, habría un error JVM). Así que no hay necesidad de verificar. La solución real debería cambiar el campotaxos
astatic
, ya que no tiene sentido reservar memoria en cada instancia para una constante de tiempo de compilación. Entonces, sitest()
esstatic
que es irrelevante.Bueno, parece que mi pregunta fue 'incorrecta' ya que no tiene nada que ver con el operador, sino más bien con el campo en sí. Aún no sé por qué ...
Lo que se convierte en:
fuente
this
referenciasnull
? ¿Sería esto posible?Integer
alguna manera, ¿y esto es el resultado del autoboxing?ALOAD 0
referenciathis
? Por lo tanto, tendría sentido (no realmente) que el compilador agregue un nullcheckthis
? Genial: /javac
para verificar mañana; y si eso también muestra este comportamiento, creo que podría ser un error de javac?En primer lugar, aquí hay un ejemplo mínimo reproducible de este comportamiento:
El comportamiento se debe a cómo el compilador de Java optimiza las constantes de tiempo de compilación .
Tenga en cuenta que en el código de byte
foo()
no se accede a ninguna referencia de objeto para obtener el valor debar
. Esto se debe a que es una constante de tiempo de compilación y, por lo tanto, la JVM simplemente puede ejecutar laiconst_5
operación para devolver este valor.Al cambiar
bar
a una constante de tiempo sin compilación (ya sea eliminando lafinal
palabra clave o no inicializando dentro de la declaración pero dentro del constructor) obtendría:donde
aload_0
empuja la referencia dethis
en la pila de operandos para luego obtener elbar
campo de este objeto.Aquí el compilador es lo suficientemente inteligente como para notar que
aload_0
(lathis
referencia en el caso de funciones miembro) lógicamente no puede serlonull
.¿Ahora su caso es realmente una optimización del compilador que falta?
Ver la respuesta de @maaartinus.
fuente