c# .net performance optimization premature-optimization

c# - ¿Cuándo es la optimización prematura?



.net performance (10)

Veo que este término se usa mucho, pero siento que la mayoría de la gente lo usa por pereza o por ignorancia. Por ejemplo, estaba leyendo este artículo:

http://blogs.msdn.com/b/ricom/archive/2006/09/07/745085.aspx

donde habla sobre sus decisiones que toma para implementar los tipos necesarios para su aplicación.

Si fuera yo, hablando de estos para el código que necesitamos escribir, otros programadores pensarían:

  1. Estoy pensando demasiado cuando no hay nada y, por lo tanto, optimizando prematuramente.
  2. Exagerar los detalles insignificantes cuando no hay desaceleraciones o problemas de rendimiento experimentados.

o ambos.

y sugeriría simplemente implementarlo y no preocuparse por esto hasta que se convierta en un problema.

¿Cuál es más preferencial?

¿Cómo hacer la diferenciación entre la optimización prematura frente a la toma de decisiones informada para una aplicación de rendimiento crítico antes de realizar cualquier implementación?


Al comenzar, solo entregar un producto es más importante que optimizarlo.

Con el tiempo, vas a perfilar varias aplicaciones y aprenderás habilidades de codificación que conducirán naturalmente a un código optimizado. Básicamente, en algún momento podrás detectar posibles puntos problemáticos y construir cosas en consecuencia.

Sin embargo, no te preocupes hasta que hayas encontrado un problema real.


Cuando tienes menos de 10 años de experiencia en codificación.


Este proverbio no (creo) se refiere a optimizaciones que están integradas en un buen diseño a medida que se crea. Se refiere a tareas específicamente dirigidas al rendimiento, que de otro modo no se realizarían.

Este tipo de optimización no se "vuelve" prematuro, de acuerdo con la sabiduría común: es culpable hasta que se demuestre su inocencia.


La optimización es difícil. Considere los siguientes ejemplos:

  1. Decidir implementar dos servidores, cada uno haciendo su propio trabajo, en lugar de implementar un solo servidor que hará ambos trabajos.
  2. Decidir ir con un DBMS y no otro, por motivos de rendimiento.
  3. Decidir usar una API específica no portátil cuando existe un estándar (por ejemplo, el uso de la funcionalidad específica de Hibernate cuando básicamente se necesita la JPA estándar) por motivos de rendimiento.
  4. Codificando algo en ensamblaje por motivos de rendimiento.
  5. Bucles desenrollados por razones de rendimiento.
  6. Escribir un fragmento de código muy rápido pero oscuro.

Mi línea de fondo aquí es simple. La optimización es un término amplio. Cuando las personas hablan de una optimización prematura, no significa que solo necesites hacer lo primero que se te venga a la mente sin considerar la imagen completa. Ellos dicen que deberías:

  1. Concéntrese en la regla 80/20: no considere TODOS los casos posibles, pero los más probables.
  2. No sobre-diseñar cosas sin una buena razón.
  3. No escriba código que no sea claro, simple y de fácil mantenimiento si no hay un problema de rendimiento real e inmediato con él.

Realmente todo se reduce a tu experiencia. Si eres un experto en procesamiento de imágenes y alguien te pide que hagas algo que hiciste diez veces antes, probablemente impulsarás todas tus optimizaciones conocidas desde el principio, pero eso estaría bien. La optimización prematura es cuando intenta optimizar algo cuando, para empezar, no sabe que necesita optimización. La razón de esto es simple: es arriesgado, está perdiendo el tiempo y será menos sostenible. Entonces, a menos que tenga experiencia y haya pasado por ese camino antes, no optimice si no sabe que hay un problema.


La optimización es prematura si:

  1. Su aplicación no está haciendo nada de tiempo crítico. (Lo que significa que, si está escribiendo un programa que suma 500 números en un archivo, la palabra "optimización" ni siquiera debería aparecer en su cerebro, ya que todo lo que hará es perder el tiempo).

  2. Estás haciendo algo crítico en el tiempo en algo que no sea el ensamblaje, y todavía te preocupa si i++; i++; i++; i++; es más rápido o i += 2 ... si es realmente tan crítico, estarías trabajando en el ensamblaje y no perder el tiempo preocupándote por esto. (Incluso entonces, este ejemplo particular probablemente no importará).

  3. Tienes la corazonada de que una cosa puede ser un poco más rápida que la otra, pero necesitas buscarla. Por ejemplo, si algo te está molestando acerca de si StopWatch es más rápido o Environment.TickCount , es una optimización prematura, ya que si la diferencia fuera mayor, probablemente estarías más seguro y no necesitarías buscarla.

Si adivina que algo puede ser lento pero no está seguro, solo ponga un //NOTE: Performance? comente, y si luego se encuentra con cuellos de botella, verifique tales lugares en su código. Personalmente, no me preocupo por las optimizaciones que no son demasiado obvias; Solo uso un generador de perfiles más tarde, si es necesario.

Otra técnica:

Simplemente ejecuto mi programa, lo analizo al azar con el depurador y veo dónde se detuvo; donde sea que se detenga es probable que sea un cuello de botella, y cuanto más se detiene ahí, peor es el cuello de botella. Funciona casi como magia. :)


La optimización prematura está haciendo una optimización del rendimiento a costa de algún otro atributo positivo de su código (por ejemplo, legibilidad) antes de saber que es necesario hacer esta compensación.

Por lo general, las optimizaciones prematuras se realizan durante el proceso de desarrollo sin utilizar ninguna herramienta de creación de perfiles para encontrar cuellos de botella en el código. En muchos casos, la optimización hará que el código sea más difícil de mantener y, a veces, también aumenta el tiempo de desarrollo y, por lo tanto, el costo del software. Peor aún ... algunas optimizaciones prematuras resultan no hacer que el código sea más rápido y, en algunos casos, incluso pueden hacer que el código sea más lento de lo que era antes.


Tener (mucha) experiencia puede ser una trampa. Conozco a muchos programadores con mucha experiencia (C / C ++, assembly) que tienden a preocuparse demasiado porque están acostumbrados a preocuparse por las marcas de reloj y los bits superfluos.

Existen áreas tales como sistemas integrados o en tiempo real en los que sí cuentan, pero en las aplicaciones normales OLTP / LOB la mayor parte de su esfuerzo debe estar dirigido hacia la facilidad de mantenimiento, la legibilidad y la capacidad de cambio.


Tenga en cuenta que la optimización no es gratuita (como en la cerveza)

  • lleva más tiempo escribir
  • lleva más tiempo leer
  • toma más tiempo para probar
  • lleva más tiempo depurar
  • ...

Entonces, antes de optimizar cualquier cosa, debes estar seguro de que vale la pena.

Ese tipo de Point3D al que te has vinculado parece ser la piedra angular de algo, y el caso de la optimización era probablemente obvio.

Al igual que los creadores de la biblioteca .NET no necesitaban ninguna medida antes de comenzar a optimizar System.String. Tendrían que medir durante todo el tiempo.

Pero la mayoría del código no juega un papel importante en el rendimiento del producto final. Y eso significa que cualquier esfuerzo de optimización se desperdicia.

Además de todo eso, la mayoría de las ''optimizaciones prematuras'' son ataques no comprobados / no medidos.


Las optimizaciones son prematuras si pasa demasiado tiempo diseñando esas durante las primeras fases de implementación. Durante las primeras etapas, tiene cosas mejores de qué preocuparse: la implementación del código central, las pruebas unitarias, los sistemas que hablan entre sí, la interfaz de usuario y todo lo demás. La optimización tiene un precio, y es posible que esté perdiendo el tiempo optimizando algo que no tiene que ser, al mismo tiempo que crea un código que es más difícil de mantener.

Las optimizaciones solo tienen sentido cuando tienes requisitos concretos de rendimiento para tu proyecto, y luego el rendimiento será importante después del desarrollo inicial y tienes suficiente de tu sistema implementado para medir realmente lo que sea que necesites medir. Nunca optimice sin medir.

A medida que adquiere más experiencia, puede realizar sus primeros diseños e implementaciones con un ojo pequeño hacia las optimizaciones futuras, es decir, intente diseñar de tal forma que sea más fácil medir el rendimiento y optimizarlo más adelante, incluso si fuera necesario. . Pero incluso en este caso, debe dedicar poco tiempo a las optimizaciones en las primeras fases de desarrollo.


La optimización es el proceso de hacer que el código existente se ejecute de manera más eficiente (velocidad más rápida, y / o menos uso de recursos)

Toda optimización es prematura si el programador no ha demostrado que es necesario. (Por ejemplo, ejecutando el código para determinar si logra los resultados correctos en un marco de tiempo aceptable. Esto podría ser tan simple como ejecutarlo para "ver" si se ejecuta lo suficientemente rápido, o ejecutar bajo un generador de perfiles para analizarlo con más cuidado) .

Hay varias etapas para programar algo bien:

1) Diseña la solución y elige un algoritmo bueno y eficiente .

2) Implemente la solución de una manera fácil de mantener y codificada.

3) Pruebe la solución y vea si cumple con sus requisitos de velocidad, uso de RAM, etc. (por ejemplo, "Cuando el usuario hace clic en" Guardar ", ¿tarda menos de 1 segundo?" Si toma 0.3s, realmente no lo hace " Necesito pasar una semana optimizándolo para bajar ese tiempo a 0.2 s)

4) Si no cumple con los requisitos, considere por qué. En la mayoría de los casos, esto significa ir al paso (1) para encontrar un mejor algoritmo ahora que comprende mejor el problema. (Escribir un prototipo rápido es a menudo una buena forma de explorar esto a bajo costo)

5) Si aún no cumple con los requisitos, comience a considerar optimizaciones que pueden ayudar a acelerar el tiempo de ejecución (por ejemplo, tablas de búsqueda, almacenamiento en caché, etc.). Para impulsar este proceso, la creación de perfiles suele ser una herramienta importante para ayudarlo a encontrar los cuellos de botella e ineficiencias en el código, de modo que pueda obtener la mayor ganancia por el tiempo que gasta en el código.

Debo señalar que un programador experimentado que trabaje en un problema razonablemente familiar puede pasar por los primeros pasos mentalmente y luego simplemente aplicar un patrón, en lugar de pasar físicamente por este proceso cada vez, pero esto es simplemente un atajo que es adquirido a través de la experiencia

Por lo tanto, hay muchas "optimizaciones" que los programadores experimentados construirán en su código automáticamente. Estas no son "optimizaciones prematuras" sino "patrones de eficiencia de sentido común". Estos patrones son rápidos y fáciles de implementar, pero mejoran enormemente la eficiencia del código, y no es necesario realizar ninguna prueba de tiempo especial para determinar si serán o no beneficiosos:

  • No poner código innecesario en los bucles. (Similar a la optimización de eliminar código innecesario de bucles existentes, pero ¡no implica escribir el código dos veces!)
  • Almacenar resultados intermedios en variables en lugar de volver a calcular las cosas una y otra vez.
  • Usar tablas de búsqueda para proporcionar valores precalculados en lugar de calcularlos sobre la marcha.
  • El uso de estructuras de datos de tamaño apropiado (por ejemplo, almacenar un porcentaje en un byte (8 bits) en lugar de uno largo (64 bits) utilizará 8 veces menos RAM)
  • Dibujar un fondo de ventana complejo usando una imagen prediseñada en lugar de dibujar muchos componentes individuales
  • Aplicación de compresión a los paquetes de datos que desea enviar a través de una conexión de baja velocidad para minimizar el uso del ancho de banda.
  • Dibujar imágenes para su página web en un estilo que le permita usar un formato que obtendrá alta calidad y buena compresión.
  • Y, por supuesto, aunque técnicamente no es una "optificación", ¡elegir el algoritmo correcto en primer lugar!

Por ejemplo, acabo de reemplazar una pieza antigua de código en nuestro proyecto. Mi nuevo código no está "optimizado" de ninguna manera, pero (a diferencia de la implementación original) fue escrito teniendo en cuenta la eficiencia. El resultado: el mío funciona 25 veces más rápido, simplemente al no ser un desperdicio. ¿Podría optimizarlo para hacerlo más rápido? Sí, podría obtener fácilmente otra aceleración 2x. ¿Optimizaré mi código para hacerlo más rápido? No, una mejora de velocidad 5x hubiera sido suficiente, y ya he logrado 25x. Trabajar más en este punto sería un desperdicio de tiempo de programación. (Pero puedo volver a visitar el código en el futuro si cambian los requisitos)

Finalmente, un último punto: el área en la que está trabajando dicta la barra que debe cumplir. Si está escribiendo un motor gráfico para un juego o código para un controlador incrustado en tiempo real, es posible que se encuentre optimizando mucho. Si está escribiendo una aplicación de escritorio como un bloc de notas, es posible que nunca necesite optimizar nada siempre que no sea demasiado derrochador.