c# - Uso adecuado de 'rendimiento'



8 Answers

Completar una lista temporal es como descargar todo el video, mientras que usar el yield es como transmitir ese video.

c# yield-return

La palabra clave de yield es una de esas keywords en C # que continúa desconcertándome, y nunca he estado seguro de que la esté usando correctamente.

De los siguientes dos códigos, ¿cuál es el preferido y por qué?

Versión 1: Usando retorno de rendimiento

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

Versión 2: Devolver la lista

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}



Esto va a parecer una sugerencia extraña, pero aprendí a usar la palabra clave de yield en C # leyendo una presentación sobre generadores en Python: http://www.dabeaz.com/generators/Generators.pdf David M. Beazley. No necesitas saber mucho de Python para entender la presentación, no lo hice. Lo encontré muy útil para explicar no solo cómo funcionan los generadores, sino también por qué debería importarle.




Las dos piezas de código realmente están haciendo dos cosas diferentes. La primera versión atraerá a los miembros a medida que los necesite. La segunda versión cargará todos los resultados en la memoria antes de comenzar a hacer algo con ella.

No hay una respuesta correcta o incorrecta a esta. Cuál es preferible solo depende de la situación. Por ejemplo, si hay un límite de tiempo que tiene para completar su consulta y necesita hacer algo semi-complicado con los resultados, la segunda versión podría ser preferible. Pero tenga cuidado con los conjuntos de resultados grandes, especialmente si está ejecutando este código en modo de 32 bits. He sido mordido por las excepciones de OutOfMemory varias veces al hacer este método.

Sin embargo, la clave a tener en cuenta es lo siguiente: las diferencias están en la eficiencia. Por lo tanto, es probable que deba elegir el que simplifique el código y cambiarlo solo después del perfilado.




Esto es lo que Chris Sells cuenta sobre esas declaraciones en The C # Programming Language ;

A veces olvido que el rendimiento no es lo mismo que el rendimiento, ya que el código después de un rendimiento puede ejecutarse. Por ejemplo, el código después de la primera vuelta aquí nunca se puede ejecutar:

    int F() {
return 1;
return 2; // Can never be executed
}

En contraste, el código después de la primera devolución de rendimiento aquí se puede ejecutar:

IEnumerable<int> F() {
yield return 1;
yield return 2; // Can be executed
}

Esto a menudo me muerde en una declaración if:

IEnumerable<int> F() {
if(...) { yield return 1; } // I mean this to be the only
// thing returned
yield return 2; // Oops!
}

En estos casos, recordar que la rentabilidad del rendimiento no es "final", como la devolución es útil.




Suponiendo que la clase LINQ de sus productos utiliza un rendimiento similar para la enumeración / iteración, la primera versión es más eficiente porque solo genera un valor cada vez que se repite la iteración.

El segundo ejemplo es convertir el enumerador / iterador en una lista con el método ToList (). Esto significa que itera manualmente sobre todos los elementos en el enumerador y luego devuelve una lista plana.




¿Y qué hay de esto?

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList();
    }
}

Supongo que esto es mucho más limpio. Sin embargo, no tengo VS2008 a mano para verificar. En cualquier caso, si Productos implementa IEnumerable (como parece - se usa en una declaración foreach), lo devolvería directamente.




Devuelve la lista directamente. Beneficios:

  • Es mas claro
  • La lista es reutilizable. (el iterador no lo es) en realidad no es cierto, gracias Jon

Debe usar el iterador (rendimiento) cuando crea que probablemente no tendrá que recorrer todo el camino hasta el final de la lista, o cuando no tenga fin. Por ejemplo, la llamada del cliente buscará el primer producto que satisfaga algún predicado, podría considerar usar el iterador, aunque ese es un ejemplo artificial, y probablemente haya mejores formas de lograrlo. Básicamente, si sabe de antemano que será necesario calcular toda la lista, simplemente hágalo por adelantado. Si crees que no lo hará, entonces considera usar la versión de iterador.




El uso del rendimiento es similar al retorno de la palabra clave, excepto que devolverá un generator . Y el objeto generador solo atravesará una vez .

El rendimiento tiene dos beneficios:

  1. No necesita leer estos valores dos veces;
  2. Puede obtener muchos nodos secundarios pero no tiene que guardarlos todos en la memoria.

Hay otra explanation clara tal vez te ayude.




Related