2012-02-24 10 views
8

entiendo que la ejecución de las operaciones en diferentes órdenes implica un funcionamiento diferente, como la diferencia entre la siguiente consulta lenta:¿El orden del predicado es importante cuando se usa LINQ?

List<TestItem> slowResults = items.OrderBy(item => item.StringItem) 
            .Where(item => item.IntItem == 100) 
            .ToList(); 

y esto uno más rápido:

List<TestItem> fastResults = items.Where(item => item.IntItem == 100) 
            .OrderBy(item => item.StringItem) 
            .ToList(); 

Pero eso no es mi pregunta:

Mi pregunta es sobre el rendimiento del cortocircuito ya que relaciona tes a un predicado LINQ. Cuando uso una cláusula WHERE, como en este caso:

List<TestItem> results = items.Where(item => item.Item1 == 12 && 
              item.Item2 != null && 
              item.Item2.SubItem == 65 && 
              item.Item3.Equals(anotherThingy)) 
           .ToList(); 

¿No importa el orden de los argumentos? Por ejemplo, esperaría que hacer un .Equals primero resulte en una consulta más lenta en general debido a que la evaluación entera Item1 == 12 es una operación mucho más rápida?

Si el orden es importante, ¿cuánto importa? Por supuesto, los métodos de llamada como .Equals probablemente resulten en una desaceleración mucho más grande que si simplemente estuviera comparando algunos enteros, ¿pero es una penalización de rendimiento relativamente pequeña en comparación con la forma en que opera LINQ "lento"? Como LINQ hace toneladas de llamadas a métodos, algo como .Equals realmente va a importar, ya que, a menos que sea anulado, estará ejecutando código de framework nativo, ¿no? Por otro lado, ¿la llamada a un método MSIL estándar va a ser significativamente más lenta?

Además, ¿hay otras optimizaciones del compilador en esta consulta que podrían acelerar esto bajo el capó?

Gracias por los pensamientos y la aclaración! Brett

+2

¿LINQ a qué? – SLaks

+0

Estaba pensando en LINQ to Objects, pero supongo que la pregunta es aún más importante para LINQ to SQL. – Brett

+2

Aunque no conozco LINQ____, si es como otros lenguajes en cortocircuito, el orden es importante, porque si la expresión que se va a evaluar alguna vez se convierte definitivamente en verdadera o definitivamente falsa, el resto de los predicados se pueden descartar con seguridad. Por ejemplo, en la expresión '(1 == 1 || x == 3)', 'x == 3' nunca se evaluará porque no importa cuál sea el resultado, la expresión será verdadera. Un ejemplo simple similar podría ser '(1 == 0 && x == 3)', donde después de que '1 == 0' se encuentre como falso, simplemente se cierra porque no hay forma de que la expresión sea verdadera. Lo siento si esto no es lo que estás buscando! – prelic

Respuesta

13

La respuesta va a ser diferente para los diferentes proveedores de LINQ. En particular, la historia es muy diferente para LINQ to Objects y dice LINQ to Entities.

En LINQ to Objects, el operador Where acepta el filtro como Func < TSource, bool >. Func <, > es un delegado, por lo que para los fines de esta discusión, puede considerarlo como un puntero de función. En LINQ a objetos, la consulta equivale a esto:

static void Main() { 
    List<TestItem> results = items.Where(MyFilter).ToList(); 

static boolean MyFilter(TestItem item) { 
    return item.Item1 == 12 && 
     item.Item2 != null && 
     item.Item2.SubItem == 65 && 
     item.Item3.Equals(anotherThingy) 
} 

Lo más importante a notar es que myFilter es un método ordinario C# y aplicar las reglas de modo ordinario C#, incluyendo el comportamiento cortocircuitos de & &. En consecuencia, las condiciones se evaluarán en el orden en que las escribió. LINQ to Objects puede invocar MyFilter en diferentes elementos de entrada, pero no puede cambiar lo que hace MyFilter.

En LINQ a Entidades y LINQ to SQL, el operador Cuando acepta el filtro como Expresión < Func < TSource, bool > >. Ahora, el filtro pasa al operador Where como una estructura de datos que describe la expresión. En ese caso, el proveedor de LINQ observará la estructura de datos (el "árbol de expresión") y le corresponde al proveedor de LINQ decidir cómo interpretarlo.

En los casos de LINQ to Entities y LINQ to SQL, el árbol de expresiones se traducirá a SQL. Y luego depende del servidor de la base de datos decidir cómo ejecutar la consulta.El servidor definitivamente puede reordenar las condiciones, y puede hacer optimizaciones aún más sustanciales. Por ejemplo, si la tabla SQL contiene un índice en una de las columnas a las que se hace referencia en la condición, el servidor puede elegir usar el índice y evitar incluso mirar las filas que no coinciden con esa parte de condición particular.

+0

¡Gracias por la respuesta detallada! – Brett

Cuestiones relacionadas