java arrays premature-optimization

java - ¿Cuál es el costo de llamar a array.length



arrays premature-optimization (8)

Array.length es una constante y el compilador JIT debe verlo en ambas instancias. Esperaría que el código de la máquina resultante sea el mismo en ambos casos. Al menos para el compilador del servidor.

Al actualizar los bucles para cada bucle en nuestra aplicación, encontré muchos de estos "patrones":

for (int i = 0, n = a.length; i < n; i++) { ... }

en lugar de

for (int i = 0; i < a.length; i++) { ... }

Veo que obtiene rendimiento para las colecciones porque no necesita llamar al método size () con cada ciclo. Pero con arreglos?

Entonces surgió la pregunta: ¿es array.length más costoso que una variable regular?


Dudo que haya una diferencia significativa, y aunque existiera, apostaría que probablemente se optimizó durante la compilación. Estás perdiendo el tiempo cuando intentas micro-optimizar cosas así. Primero, haga que el código sea legible y correcto; luego, si tiene un problema de rendimiento, use un generador de perfiles , luego preocúpese de elegir mejores estructuras de datos / algoritmos, y luego preocúpese de optimizar las partes que resalta su generador de perfiles.


En ese caso, ¿por qué no haces un ciclo inverso?

for (int i = a.length - 1; i >= 0; --i) { ... }

Hay 2 micro optimizaciones aquí:

  • Inversión de bucle
  • Decremento de posfijo

Esta respuesta es desde un punto de vista de C #, pero creo que lo mismo se aplica a Java.

En C #, el modismo

for (int i = 0; i < a.length; i++) { ...}

se reconoce como una iteración sobre la matriz, por lo que se evita la verificación de límites cuando se accede a la matriz en el bucle en lugar de con cada acceso a la matriz.

Esto puede o no ser reconocido con un código tal como:

for (int i = 0, n = a.length; i < n; i++) { ...}

o

n = a.length; for (int i = 0; i < n; i++) { ...}

No sé cuánto de esta optimización realiza el compilador frente a JITter, y en particular si es realizada por JITter, espero que los 3 generen el mismo código nativo.

Sin embargo, la primera forma también podría decirse que es más legible por las personas, así que yo diría que síganlo.


La longitud de una matriz se almacena como una variable miembro de la matriz (que no es lo mismo que un elemento) en Java, por lo que recuperar esa longitud es una operación de tiempo constante, lo mismo que leer una variable miembro de una clase normal. Muchos lenguajes antiguos como C y C ++ no almacenan la longitud como parte de la matriz, por lo que querrá almacenar la longitud antes de que comience el ciclo. No tienes que hacer eso en Java.


No, una llamada a array.length es O(1) o una operación de tiempo constante.

Como el .length es (actúa como) un miembro final public de la array , no es más lento acceder que una variable local. (Es muy diferente de una llamada a un método como size() )

Es probable que un compilador moderno optimice la llamada a .length todos modos.


Puede ser cada vez más rápido almacenarlo en una variable. Pero estaría extremadamente sorprendido si un generador de perfiles lo señalara como un problema.

En el nivel de bytecode, obtener la longitud de una matriz se hace con el bytecode arraylength . No sé si es más lento que un bytecode iload , pero no debería haber suficiente diferencia como para notarlo.


Tuve un poco de tiempo durante el almuerzo:

public static void main(String[] args) { final int[] a = new int[250000000]; long t; for (int j = 0; j < 10; j++) { t = System.currentTimeMillis(); for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; } System.out.println("n = a.length: " + (System.currentTimeMillis() - t)); t = System.currentTimeMillis(); for (int i = 0; i < a.length; i++) { int x = a[i]; } System.out.println("i < a.length: " + (System.currentTimeMillis() - t)); } }

Los resultados:

n = a.length: 672 i < a.length: 516 n = a.length: 640 i < a.length: 516 n = a.length: 656 i < a.length: 516 n = a.length: 656 i < a.length: 516 n = a.length: 640 i < a.length: 532 n = a.length: 640 i < a.length: 531 n = a.length: 641 i < a.length: 516 n = a.length: 656 i < a.length: 531 n = a.length: 656 i < a.length: 516 n = a.length: 656 i < a.length: 516

Notas:

  1. Si invierte las pruebas, entonces n = a.length muestra que es más rápido que i < a.length en aproximadamente la mitad, probablemente debido a la recolección de basura (?).
  2. No pude hacer 250000000 mucho más grande porque obtuve OutOfMemoryError en 270000000 .

El punto es, y es el que todos los demás han estado haciendo, tienes que ejecutar Java sin memoria y todavía no ves una diferencia significativa en la velocidad entre las dos alternativas. Dedique su tiempo de desarrollo a las cosas que realmente importan.