2010-06-09 28 views
5

He estado luchando con una aplicación que estoy escribiendo y creo que estoy empezando a ver que mi problema es la optimización prematura. El lado perfeccionista de mí quiere que todo sea óptimo y perfecto la primera vez, pero estoy descubriendo que esto complica el diseño bastante. En lugar de escribir pequeñas funciones comprobables que hacen bien una simple cosa, me inclino por abarcar toda la funcionalidad posible para ser más eficiente.¿Estoy entendiendo la optimización prematura correctamente?

Por ejemplo, estoy evitando viajes múltiples a la base de datos por la misma información a costa de que mi código se vuelva más complejo. Una parte de mí simplemente no quiere preocuparse por las llamadas a bases de datos redundantes. Haría más fácil escribir el código correcto y la cantidad de datos que se obtienen es pequeña de todos modos. La otra parte de mí se siente muy sucia e impura al hacer esto. :-)

Me inclino por simplemente ir a la base de datos varias veces, lo cual creo que es la mudanza correcta aquí. Es más importante que termine el proyecto y siento que me estoy colgando debido a optimizaciones como esta. Mi pregunta es: ¿es esta la estrategia correcta para usar al evitar una optimización prematura?

+0

Ha habido mucha discusión sobre esto antes: http://stackoverflow.com/search?q=premature+optimization –

+0

Si nos dices qué idioma y base de datos estás utilizando, las personas podrían ser ilustrativas ejemplos. Me doy cuenta de que su pregunta es general, pero los ejemplos a menudo ayudan. – detly

+0

@detly: es una aplicación web respaldada por php/mysql. –

Respuesta

20

Esta es la estrategia correcta en general. Haga que el código funcione, completamente cubierto con pruebas automáticas.

Luego puede ejecutar las pruebas automatizadas mientras el programa está bajo el control de un generador de perfiles, para averiguar dónde está gastando tiempo y/o memoria el programa. Eso te mostrará dónde optimizar.

Y le mostrará cómo optimizar código de trabajo, no el código que puede funcionar o no cuando está todo junto.

No desea un código que falle de manera óptima.


La cita estaba fallando para recordar es de Michigan Ravera:

Si esto no funciona, no importa lo rápido que no funciona.

+12

"No quiere código que falle de manera óptima". - ¡excelente manera de decirlo! –

+0

@Paperjam: alguien más dijo algo similar. Simplemente no podía recordar exactamente qué. –

5

Debemos olvidarnos de pequeñas eficiencias, dicen que alrededor del 97% del tiempo: la optimización prematura es la raíz de todo mal. - Hoare

Mientras que @John Saunders lo clava, la aplicación de TDD sola podría no resolver completamente sus preocupaciones. Me adhiero a TDD, y cuando haces TDD correctamente, y si puedes aplicar la refactorización de manera efectiva, normalmente terminas con un código mucho más simple y con la ventaja de que sabes que funciona. No hay argumentos allí.

Sin embargo, veo que muchos desarrolladores escriben código ignorante del rendimiento, evitando la optimización prematura no es excusa para escribir código descuidado/flojo/ingenuo. Las pruebas de unidad de escritura no impiden esto. Aunque alguien que escribe pruebas unitarias es probablemente un mejor codificador, y los mejores codificadores son menos aptos para escribir código incorrecto con tanta frecuencia.

Do pruebas de escritura, y hacer incluyen pruebas de rendimiento en su conjunto de pruebas para los escenarios de sus grupos de interés se identifican. p.ej.recuperar 100 productos con descuento para un proveedor específico, e incluyen los niveles de existencias y el formato como XML en menos de 3 segundos

La falacia de que "la optimización prematura" es lo mismo que "preocupación por el rendimiento" no debe guiar el software desarrollo. - Randall Hyde

Si deja las preocupaciones de rendimiento demasiado tarde, puede encontrar que es demasiado difícil o demasiado costoso cambiar.

Algunos artículos

+0

Me temo que debo estar en desacuerdo en parte. Escribir "lo más simple que podría funcionar" puede llevar a escribir un código ingenuo. Pero al usar TDD obtendrás una excelente cobertura de código, lo suficiente como para que puedas utilizar esas pruebas para conducir la investigación de rendimiento y el proceso de corrección. –

+0

@ John Saunders No sé con qué estás en desacuerdo? Mi respuesta intenta alentar al OP a escribir pruebas basadas en las expectativas de desempeño de las partes interesadas. He reformulado para aclarar. –

+0

Podría aclarar que su código debe pasar todas las pruebas, incluidas las de rendimiento. Sin embargo, se debe lograr un equilibrio razonable entre lograr que el código funcione y que el código funcione bien. Es posible que haya que rediseñar el código de bajo rendimiento para cumplir los objetivos de rendimiento. Pero si pasa todas las pruebas automatizadas, entonces el rediseño puede proceder como una refactorización, con mucha mayor confianza de que el código aún funciona. –

1

El aspecto clave de la cita de Knuth para mí es "penique sabio y libra tonta". Así es como finalmente describió al optimizador prematuro: alguien que regatea por ahorrar centavos cuando hay libras para ahorrar, y que lucha por mantener su software "optimizado" (preste especial atención a cómo utilizó las comillas aquí).

Encuentro que muchas personas a menudo solo citan una pequeña parte del artículo de Knuth. Vale la pena darse cuenta de que su periódico argumentaba que debía usar goto para acelerar las rutas de ejecución críticas en el software.

Una cita más completa:

[...] esto es un ahorro notable en la velocidad de funcionamiento global, si, por ejemplo, el valor medio de n es aproximadamente 20, y si se lleva a cabo la rutina de búsqueda aproximadamente un millón de veces en el programa. Tales optimizaciones de bucle [usando gotos] no son difíciles de aprender y, como ya he dicho, son apropiadas solo en una pequeña parte de un programa, pero a menudo producen ahorros sustanciales. [...]

La sabiduría convencional compartida por muchos de los ingenieros de software de hoy llama a ignorar la eficiencia en el pequeño; pero creo que esto es simplemente una reacción exagerada a los abusos que ven que se practican por programadores de centavo y libra-tonto, que no pueden depurar o mantienen sus programas "optimizados". En las disciplinas de ingeniería establecidas , una mejora del 12%, de fácil obtención, nunca se considera marginal; y creo que el mismo punto de vista debería prevalecer en la ingeniería del software . Por supuesto, no me molestaría en hacer tales optimizaciones en un trabajo único, pero cuando se trata de preparar programas de calidad, no quiero restringirme a herramientas que me nieguen dichas eficiencias .

No hay duda de que el grial de la eficiencia conduce al abuso. Los programadores pierden enormes cantidades de tiempo pensando o preocupando sobre la velocidad de las partes no críticas de sus programas, y estos intentos de eficacia tienen un fuerte impacto negativo cuando se consideran la eliminación y el mantenimiento de . Deberíamos olvidar las pequeñas eficiencias , digamos el 97% del tiempo; La optimización prematura es la raíz de todos los males.

A menudo es un error hacer juicios a priori sobre qué partes de un programa son realmente crítico, ya que la experiencia universal de programadores que han estado utilizando herramientas de medición ha sido que sus conjeturas intuitivas fallan. Después de trabajar con estas herramientas durante siete años, me he convencido de que todos los compiladores escritos de ahora en adelante deberían diseñarse para proporcionar a todos los programadores comentarios que indiquen qué partes de sus programas cuestan más; de hecho, esta retroalimentación debe ser proporcionada automáticamente a menos que haya sido específicamente desactivada.

Después de un programador sabe qué partes de sus rutinas son realmente importantes, una transformación como duplicar bucles valdrá la pena. Tenga en cuenta que esta transformación introduce sentencias go to, y también lo hacen otras optimizaciones de bucle.

Así que esto viene de alguien que en realidad era profundamente preocupados con el rendimiento en el nivel micro, y en el momento (optimizadores han conseguido mucho mejor ahora), se utiliza para goto velocidad.

En el corazón de la creación de la "optimizador prematura" de este Knuth es:

  1. Optimizar basan en corazonadas/supersticiones/intuiciones humanas sin experiencia o mediciones (de optimización a ciegas, sin saber realmente lo que está haciendo el pasado)
  2. Optimización de una manera que ahorra centavos en libras (optimizaciones ineficaces).
  3. Buscando el máximo absoluto absoluto de eficiencia para todo.
  4. Buscando eficiencia en rutas no críticas.
  5. Tratando de optimizar cuando apenas puede mantener/depurar su código.

Nada de esto tiene que ver con el momento de sus optimizaciones, sino con la experiencia y la comprensión, desde la comprensión de las rutas críticas hasta la comprensión de lo que realmente ofrece el rendimiento.

Las cuestiones como el desarrollo basado en pruebas y un enfoque predominante en el diseño de la interfaz no se cubrieron en el documento de Knuth. Estos son conceptos e ideas más modernos. Estaba concentrado en la implementación principalmente.

Sin embargo, es una buena actualización de los consejos de Knuth: intentar establecer la exactitud primero a través de pruebas y diseños de interfaz que le permitan optimizar sin romper todo.

Si tratamos de aplicar una interpretación moderna de Knuth, agregaría "enviar" allí. Incluso si está optimizando las verdaderas rutas críticas de su software con ganancias medidas, el software más rápido del mundo no tiene valor si nunca se envía. Tener eso en cuenta debería ayudarte a hacer compromisos más inteligentes.

Me estoy inclinando por simplemente ir a la base de datos varias veces, lo que I creo que es la mudanza correcta aquí. Es más importante que termine el proyecto y siento que me estoy colgando por las optimizaciones de esta manera. Mi pregunta es: ¿esta es la estrategia correcta para usar cuando evitando la optimización prematura?

Depende de usted el mejor juicio, teniendo en cuenta algunos de los puntos anteriores, ya que comprende más íntimamente sus propios requisitos.

Un factor crucial que sugeriría es que, si se trata de una ruta de rendimiento crítico para una carga pesada, diseñe sus interfaces públicas de manera que deje mucho espacio para optimizar.

Por ejemplo, no diseñe un sistema de partículas con dependencias de cliente en una interfaz Particle. Eso no deja espacio para optimizar, cuando solo tiene que trabajar con el estado encapsulado y la implementación de una sola partícula. En ese caso, es posible que deba realizar cambios en cascada en su base de código para optimizar. Un auto de carreras no puede utilizar su velocidad si el camino tiene solo 10 metros de largo. En su lugar, diseñe hacia la interfaz ParticleSystem que agrega un millón de partículas, por ejemplo, con operaciones de mayor nivel que se ocupan de partículas a granel cuando sea posible. Eso le deja mucho espacio para optimizar sin romper sus diseños si encuentra que necesita optimizar.

El lado perfeccionista de mí quiere que todo sea óptimo y perfecto la primera vez a través, pero me estoy encontrando esto está complicando el diseño un poco.

Ahora esta parte suena un poco prematura. En general, su primer paso debe ser hacia la simplicidad. La simplicidad a menudo va de la mano con razonablemente rápido, más rápido de lo que piensas incluso si estás haciendo un trabajo redundante.

De todos modos, espero que estos puntos ayuden al menos a agregar algunas cosas más a consideración.

Cuestiones relacionadas