net - C#- For-loop internas
foreach c# asp net (7)
una pregunta rápida y simple acerca de for-loops.
Situación Actualmente estoy escribiendo un código de alto rendimiento cuando de repente me preguntaba cómo se comporta realmente el for-loop. Sé que me he tropezado con esto antes, pero no puedo encontrar esta información de nuevo:
Aún así, mi principal preocupación era con el limitador. Digamos que tenemos:
for(int i = 0; i < something.awesome; i++)
{
// Do cool stuff
}
La pregunta es algo.awesome almacenado como una variable interna o es el bucle constantemente recuperando algo.¡Muy impresionante para hacer la verificación lógica? Por supuesto, lo que estoy preguntando es porque necesito revisar un montón de cosas indexadas y realmente no quiero la función adicional: llamar por encima de cada pase.
Sin embargo, si something.awesome solo se llama una vez, ¡entonces volveré a estar bajo mi rock feliz! :)
Cada vez que el compilador recupera el valor de something.awesome y lo evalúa
Evaluó todo el tiempo. Prueba esto en una aplicación de consola simple:
public class MyClass
{
public int Value
{
get
{
Console.WriteLine("Value called");
return 3;
}
}
}
Usado de esta manera:
MyClass myClass = new MyClass();
for (int i = 0; i < myClass.Value; i++)
{
}
Producirá tres líneas impresas en la pantalla.
Actualizar
Para evitar esto, podrías hacer esto:
int awesome = something.awesome;
for(int i = 0; i < awesome; i++)
{
// Do cool stuff
}
La condición se evalúa cada vez, incluso recuperando el valor de something.awesome
. something.awesome
. Si desea evitar eso, establezca una variable temp en something.awesome
y compare con la variable temp en su lugar.
Puede usar un programa de ejemplo simple para verificar el comportamiento:
using System;
class Program
{
static int GetUpperBound()
{
Console.WriteLine("GetUpperBound called.");
return 5;
}
static void Main(string[] args)
{
for (int i = 0; i < GetUpperBound(); i++)
{
Console.WriteLine("Loop iteration {0}.", i);
}
}
}
El resultado es el siguiente:
GetUpperBound called.
Loop iteration 0.
GetUpperBound called.
Loop iteration 1.
GetUpperBound called.
Loop iteration 2.
GetUpperBound called.
Loop iteration 3.
GetUpperBound called.
Loop iteration 4.
GetUpperBound called.
Los detalles de este comportamiento se describen en la Especificación del lenguaje C # 4.0, sección 8.3.3 (Encontrará la especificación dentro de C: / Archivos de programa / Microsoft Visual Studio 10.0 / VC # / Specifications / 1033 ):
Una instrucción for se ejecuta de la siguiente manera:
Si hay un inicializador for, los inicializadores de variable o las expresiones de instrucción se ejecutan en el orden en que se escriben. Este paso solo se realiza una vez.
Si hay una condición forzada, se evalúa.
Si la condición para no está presente o si la evaluación es verdadera, el control se transfiere a la declaración incrustada. Cuando y si el control llega al punto final de la declaración incrustada (posiblemente a partir de la ejecución de una instrucción continuar), las expresiones de for-iterator, si las hay, se evalúan en secuencia, y luego se realiza otra iteración, comenzando con la evaluación de la para-condición en el paso anterior.
Si la condición for está presente y la evaluación arroja un resultado falso, el control se transfiere al punto final de la proposición for.
Sería almacenar alguna variable y borrarla después de su uso.
usando (int limit = something.awesome)
{
para (int i = 0; i <limiti; i ++)
{
//Código.
}
}
De esta forma no se controlará todo el tiempo.
Si something.awesome es un campo , es probable que tenga acceso en cada ronda del bucle, ya que algo en el cuerpo del bucle puede actualizarlo. Si el cuerpo del bucle es lo suficientemente simple y no llama a ningún método (aparte de los métodos que el compilador en línea), entonces el compilador puede probar que es seguro poner el valor de algo.awesome en un registro. Los escritores del compilador acostumbraban a permitirse el lujo de hacer esto corto de cosas.
Sin embargo, en estos días se necesita mucho tiempo para acceder a un valor desde la memoria principal, pero una vez que el valor ha sido leído por primera vez, es dirigido por la CPU. Leer el valor por segunda vez desde la memoria caché de la CPU tiene una velocidad mucho más cercana a la lectura de un registro que a la lectura desde la memoria principal. No es raro que un caché de CPU sea cientos de veces más rápido que la memoria principal.
Ahora si algo.awesome es una propiedad , entonces es en efecto una llamada a método. El compilador llamará al método cada vez que pasa el ciclo. Sin embargo, si la propiedad / método es solo unas pocas líneas de código, puede estar en línea por el compilador. Inlinear es cuando el compilador ingresa una copia del código del método directamente en lugar de llamar al método, por lo que una propiedad que solo devuelva el valor de un campo se comportará igual que el ejemplo de campo anterior.
Evan cuando la propiedad no está en línea, estará en la memoria caché de la CPU después de haber sido llamada la primera vez. Por lo tanto, si es muy complejo o el ciclo se repite muchas veces, la primera vez que se pasa es mucho más larga, tal vez por más de un factor de 10.
En los viejos tiempos , solía ser fácil porque todo el acceso a la memoria y las acciones de la CPU tomaban casi el mismo tiempo. En estos días, la memoria caché del procesador puede cambiar fácilmente el tiempo para algunos accesos a la memoria y llamadas a métodos por un factor de 100 . Los perfiladores tienden a suponer que todo el acceso a la memoria toma el mismo tiempo. Entonces, si su perfil se le indicará que haga cambios que pueden no tener ningún efecto en el mundo real.
Cambiar el código a:
int limit = something.awesome;
for(int i = 0; i < limit; i++)
{
// Do cool stuff
}
En algunos casos, lo difundirá, pero también lo hará más complejo. sin embargo
int limit = myArray.length;
for(int i = 0; i < limit; i++)
{
myArray[i[ = xyn;
}
es más lento entonces
for(int i = 0; i < myArray.length; i++)
{
myArray[i[ = xyn;
}
como .net comprueba el límite de las matrices cada vez que se accede y tiene lógica para eliminar la verificación cuando el ciclo es lo suficientemente simple.
Por lo tanto, es mejor mantener el código simple y claro hasta que pueda demostrar que hay un problema. Usted gana mucho mejor si dedica tiempo a mejorar el diseño general de un sistema, esto es fácil de hacer si el código con el que comienza es simple.
something.awesome
será reevaluado cada vez que pases por el circuito.
Sería mejor hacer esto:
int limit = something.awesome;
for(int i = 0; i < limit; i++)
{
// Do cool stuff
}