2009-07-30 10 views
42

Mientras que la actualización de los bucles a fines de cada uno de los bucles en nuestra aplicación, me encontré con una gran cantidad de estos "patrones":¿Cuál es el costo de llamar Array.length

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 colecciones porque no necesita llamar al método size() con cada ciclo. Pero con arreglos?

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

+4

para las cosas buenas, utilice el segundo patrón o el de cada uno de los circuitos. ¡Por favor! –

Respuesta

60

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

Desde el .length es (actúa como un public) final miembro del array, no es más lento para el acceso a 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 de todos modos.

+7

@jjnguy: ¿No puede la llamada a 'array.length' ser O (1) y aún tomar más tiempo que' int len ​​= array.length; i

+1

Bueno, si te estás preocupando por tales cosas, deberías estar programando en C o C++. (Pero, sí, puede que tengas razón) – jjnguy

+8

@jjnguy: Simplemente siendo pedantes y señalando que todos los O (1) no son creados iguales. –

3

Puede ser siempre ligeramente 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 realiza 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.

11

Dudo que haya ninguna diferencia significativa en absoluto, y aunque existiera, apostaría que probablemente se haya optimizado 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 profiler, luego preocúpese de elegir mejores estructuras de datos/algoritmos si es necesario, y luego preocúpese de optimizar las partes que resalta su generador de perfiles.

+0

+1.A menos que estés haciendo algo increíblemente trivial dentro del ciclo, lo que sea que * estás * haciendo dentro del ciclo probablemente demorará más que acceder '.length' en órdenes de magnitud. –

7

La longitud de una matriz se almacena como una variable miembro de la matriz (no igual a 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.

+4

No es exactamente como una variable miembro; en realidad tiene su propio bytecode especial.

2

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

En C#, el idioma

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

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

Esto puede o no puede ser reconocida con el código como:

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

o

n = a.length; 

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

¿Cuánto de esta optimización se lleva a cabo por el compilador frente a la fluctuación de fase no sé y, en particular, si es ejecutado por JITter, espero que los 3 generen el mismo código nativo.

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

+0

Sí, creo que Java hace la misma optimización (al menos las JVM más recientes deberían). –

14

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 espectáculos de ser más rápido que i < a.length a 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, tiene que ejecutar Java sin memoria y todavía no se ve una diferencia significativa en la velocidad entre las dos alternativas. Dedique su tiempo de desarrollo a las cosas que realmente importan.

+0

prueba java -Xmx512M para hacer que el montón sea más grande ... – wbkang

+0

Esto importa en C/C++. –

+0

@wbkang: Identificar que mi tamaño de almacenamiento dinámico predeterminado no era suficiente para una matriz muy grande no era realmente el objetivo del ejercicio. –

1

Array.length es una constante y el compilador JIT debe ver a través de eso 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.

4

En este caso, ¿por qué no se crea un bucle inverso:

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

Existen 2 micro optimizaciones aquí:

  • reversión Loop
  • Postfix decrementar
+0

¿por qué es mejor la "inversión de bucle"? Porque la comparación a 0 es más rápida? – Stroboskop

+0

Sí, eso creo. Alguien ha hecho un punto de referencia para esto: http://www.mkyong.com/java/reverse-loop-versus-forward-loop-in-performance-java/ – instcode

+1

Y en cuanto a "¿por qué no?" - porque yo quiero mantener el código simple. El motivo por el que formulé esta pregunta fue simplemente para ver si existe alguna razón para usar el patrón descrito en la pregunta. – Stroboskop

Cuestiones relacionadas