¿El compilador de C#es lo suficientemente inteligente como para optimizar este código?
.net optimization (6)
¿Por qué no probarlo?
simplemente configure 2 aplicaciones de consola para que se vean 10 millones de veces y compare los resultados ... recuerde ejecutarlas como aplicaciones lanzadas correctamente que se han instalado correctamente o de lo contrario no puede garantizar que no solo está ejecutando el msil.
En realidad, es probable que obtenga unas 5 respuestas que digan ''no debe preocuparse por la optimización''. claramente no escriben rutinas que deben ser lo más rápidas posible antes de ser legibles (por ejemplo, juegos).
Si este fragmento de código es parte de un bucle que se ejecuta miles de millones de veces, esta optimización podría valer la pena. Por ejemplo, los resultados máximos podrían ser un método anulado y, por lo tanto, es posible que deba analizar las llamadas a métodos virtuales.
Realmente, la ÚNICA manera de responder a cualquiera de estas preguntas es averiguar si se trata de una pieza de código que se beneficiará de la optimización. Entonces necesitas saber los tipos de cosas que aumentan el tiempo de ejecución. Realmente nosotros, simples mortales, no podemos hacer esto a priori, por lo que simplemente debemos probar 2-3 versiones diferentes del código y luego probarlo.
Por favor, ignore la legibilidad del código en esta pregunta.
En términos de rendimiento, debería escribirse el siguiente código así:
int maxResults = criteria.MaxResults;
if (maxResults > 0)
{
while (accounts.Count > maxResults)
accounts.RemoveAt(maxResults);
}
o así:
if (criteria.MaxResults > 0)
{
while (accounts.Count > criteria.MaxResults)
accounts.RemoveAt(criteria.MaxResults);
}
?
Editar: criteria
es una class
, y MaxResults
es una propiedad integer simple (es decir, public int MaxResults { get { return _maxResults; } }
.
¿El compilador de C # trata a MaxResults
como una caja negra y lo evalúa todo el tiempo? ¿O es lo suficientemente inteligente como para darse cuenta de que tengo 3 llamadas a la misma propiedad sin modificación de esa propiedad entre las llamadas? ¿Qué pasa si MaxResults
es un campo?
Una de las leyes de optimización es el cálculo previo, así que instintivamente escribí este código como el primer listado, pero tengo curiosidad de saber si este tipo de cosas se me están haciendo automáticamente (una vez más, ignore la legibilidad del código).
(Nota: no estoy interesado en escuchar el argumento de la ''microoptimización'', que puede ser válido en el caso específico que he publicado. Me gustaría tener alguna teoría detrás de lo que está sucediendo o no).
En primer lugar, la única manera de responder realmente a las preguntas de rendimiento es intentarlo de ambas maneras y probar los resultados en condiciones realistas.
Dicho esto, las otras respuestas que dicen que "el compilador" no hace esta optimización porque la propiedad podría tener efectos secundarios son correctas e incorrectas. El problema con la pregunta (aparte del problema fundamental de que simplemente no se puede responder sin probarlo y medir el resultado) es que "el compilador" es en realidad dos compiladores: el compilador C #, que compila a MSIL, y el compilador JIT , que compila IL a código de máquina.
El compilador C # nunca hace este tipo de optimización; como se señaló, hacerlo requeriría que el compilador analice el código que se está llamando y verifique que el resultado que calcula no cambia durante el tiempo de vida del código del destinatario. El compilador de C # no lo hace.
El compilador JIT podría. No hay razón por la cual no podría. Tiene todo el código sentado allí. Es completamente gratis alinear el captador de propiedades, y si la fluctuación de fase determina que el captador de propiedades integrado devuelve un valor que se puede almacenar en caché en un registro y reutilizar, entonces es libre de hacerlo. (Si no desea que lo haga porque el valor podría modificarse en otro subproceso, entonces ya tiene un error de condición de carrera; solucione el error antes de preocuparse por el rendimiento).
Si el jitter realmente alinea la búsqueda de propiedades y luego registra el valor, no tengo idea. No sé prácticamente nada sobre la inestabilidad. Pero está permitido hacerlo si lo considera oportuno. Si tiene curiosidad acerca de si lo hace o no, puede (1) preguntarle a alguien que está en el equipo que escribió el jitter o (2) examinar el código jip en el depurador.
Y, por último, permítanme aprovechar esta oportunidad para señalar que la computación de los resultados una vez, almacenar el resultado y volver a usarlo no siempre es una optimización . Esta es una pregunta sorprendentemente complicada. Hay todo tipo de cosas para optimizar:
Tiempo de ejecución
tamaño del código ejecutable: esto tiene un efecto importante en el tiempo de ejecución porque el código grande tarda más tiempo en cargarse, aumenta el tamaño del conjunto de trabajo, ejerce presión sobre los cachés del procesador, la memoria RAM y el archivo de la página. A menudo, el código lento lento es a la larga más rápido que el código rápido grande en métricas importantes como el tiempo de inicio y la localidad de memoria caché.
asignación de registros: esto también tiene un efecto importante en el tiempo de ejecución, particularmente en arquitecturas como x86 que tienen un pequeño número de registros disponibles. Al registrar un valor para una reutilización rápida puede significar que hay menos registros disponibles para otras operaciones que necesitan optimización; quizás la optimización de esas operaciones sería una ganancia neta.
y así. Se vuelve realmente complicado muy rápido.
En resumen, no es posible saber si escribir el código para almacenar en caché el resultado en lugar de volverlo a calcular es en realidad (1) más rápido o (2) mejor rendimiento. Un mejor rendimiento no siempre significa hacer más rápida la ejecución de una rutina en particular. Un mejor rendimiento consiste en determinar qué recursos son importantes para el usuario (tiempo de ejecución, memoria, conjunto de trabajo, tiempo de inicio, etc.) y optimizarlos. No puede hacer eso sin (1) hablar con sus clientes para averiguar qué les importa y (2) medir realmente para ver si sus cambios están teniendo un efecto mensurable en la dirección deseada.
Será llamado y evaluado todo el tiempo. El compilador no tiene forma de determinar si un método (o getter) es determinista y puro (sin efectos secundarios).
Tenga en cuenta que la evaluación real de la propiedad puede estar delimitada por el compilador JIT, por lo que es eficaz tan rápido como un campo simple.
Es una buena práctica hacer que la evaluación de propiedades sea una operación económica. Si realiza un cálculo pesado en el getter, considere almacenar el resultado en caché manualmente, o cambiarlo a un método.
Si MaxResults
es una propiedad, entonces no, no lo optimizará, porque el getter puede tener lógica compleja, por ejemplo:
private int _maxResults;
public int MaxReuslts {
get { return _maxResults++; }
set { _maxResults = value; }
}
Vea cómo cambiaría el comportamiento si alinea su código?
Si no hay lógica ... cualquier método que hayas escrito está bien, es una diferencia muy pequeña y todo lo legible que es PARA TI (o para tu equipo) ... tú eres quien lo está mirando.
Si los criteria
son un tipo de clase, dudo que se optimice, porque otro hilo siempre podría cambiar ese valor mientras tanto. Para struct
s no estoy seguro, pero mi intuición es que no se optimizará, pero de todos modos creo que no cambiaría mucho el rendimiento en ese caso.
Sus dos ejemplos de código solo tienen la garantía de tener el mismo resultado en entornos de subproceso único, lo que .Net no es, y si MaxResults
es un campo (no una propiedad). El compilador no puede suponer, a menos que use las características de sincronización, que los criteria.MaxResults
no cambiarán durante el curso de su ciclo. Si se trata de una propiedad, no puede suponer que el uso de la propiedad no tenga efectos secundarios.
Eric Lippert señala bastante correctamente que depende mucho de lo que quiere decir con "el compilador". El compilador C # -> IL? ¿O el compilador IL -> máquina código (JIT)? Y tiene razón al señalar que el JIT bien podría optimizar el captador de propiedades, ya que tiene toda la información (mientras que el compilador C # -> IL no necesariamente). No cambiará la situación con múltiples hilos, pero es un buen punto, no obstante.