Un colega mío cometió hoy una clase llamada ThreadLocalFormat
, que básicamente movió instancias de clases de formato Java a un subproceso local, ya que no son seguras para los subprocesos y son "relativamente caras" de crear. Escribí una prueba rápida y calculé que podía crear 200,000 instancias por segundo, le pregunté si estaba creando esa cantidad, a lo que respondió "de ninguna manera cerca de esa cantidad". Es un gran programador y todos en el equipo son altamente calificados, por lo que no tenemos problemas para comprender el código resultante, pero fue claramente un caso de optimización donde no hay una necesidad real. Retrocedió el código a mi pedido. ¿Qué piensas? ¿Es este un caso de "optimización prematura" y qué tan malo es realmente?
fuente
Respuestas:
Es importante tener en cuenta la cita completa:
Lo que esto significa es que, en ausencia de problemas de rendimiento medidos, no debe optimizar porque cree que obtendrá un aumento de rendimiento. Hay optimizaciones obvias (como no hacer la concatenación de cadenas dentro de un bucle cerrado), pero cualquier cosa que no sea una optimización trivialmente clara debe evitarse hasta que pueda medirse.
Los mayores problemas con la "optimización prematura" son que puede introducir errores inesperados y puede ser una gran pérdida de tiempo.
fuente
HashSet
lugar de unaList
era prematura. El caso de uso en cuestión era una colección estáticamente inicializada cuyo único propósito era servir como una tabla de consulta. No creo que me equivoque al decir que hay una distinción al seleccionar la herramienta adecuada para el trabajo versus la optimización prematura. Creo que su publicación confirma esta filosofía:There are obvious optimizations...anything that isn't trivially clear optimization should be avoided until it can be measured.
la optimización de un HashSet se ha medido y documentado a fondo.Set
también es más semánticamente correcto e informativo queList
, por lo que hay más que el aspecto de optimización.Las micro optimizaciones prematuras son la raíz de todo mal, porque las micro optimizaciones dejan de lado el contexto. Casi nunca se comportan como se espera.
¿Cuáles son algunas buenas optimizaciones tempranas en el orden de importancia?
Algunas optimizaciones del ciclo de desarrollo medio:
Algunas optimizaciones del ciclo de desarrollo final
No todas las optimizaciones iniciales son malas, las micro optimizaciones son malas si se realizan en el momento equivocado del ciclo de vida del desarrollo , ya que pueden afectar negativamente a la arquitectura, pueden afectar negativamente a la productividad inicial, pueden ser irrelevantes en cuanto al rendimiento o incluso tener un efecto perjudicial al final de desarrollo debido a diferentes condiciones ambientales.
Si el rendimiento es motivo de preocupación (y siempre debería serlo), siempre piense en grande . El rendimiento es una imagen más grande y no se trata de cosas como: ¿debería usar int o long ? Vaya de arriba hacia abajo cuando trabaje con rendimiento en lugar de abajo hacia arriba .
fuente
La optimización sin la primera medición es casi siempre prematura.
Creo que eso es cierto en este caso, y cierto también en el caso general.
fuente
La optimización es "malvada" si causa:
En su caso, parece que ya se dedicó un poco de tiempo al programador, el código no era demasiado complejo (una suposición de su comentario que todos los miembros del equipo podrían entender), y el código es un poco más a prueba de futuro (siendo hilo seguro ahora, si entendí tu descripción). Suena como un poco malvado. :)
fuente
Me sorprende que esta pregunta tenga 5 años y, sin embargo, nadie haya publicado más de lo que Knuth tenía que decir que un par de oraciones. Los dos párrafos que rodean la famosa cita lo explican bastante bien. El documento que se cita se llama " Programación estructurada con declaraciones ", y aunque tiene casi 40 años, trata sobre una controversia y un movimiento de software que ya no existen, y tiene ejemplos en lenguajes de programación que muchas personas nunca han visto. oído hablar, una cantidad sorprendentemente grande de lo que dijo todavía se aplica.
Aquí hay una cita más grande (de la página 8 del pdf, página 268 en el original):
Otra buena parte de la página anterior:
fuente
A menudo he visto esta cita utilizada para justificar un código obviamente malo o un código que, si bien no se ha medido su rendimiento, probablemente podría hacerse más rápido con bastante facilidad, sin aumentar el tamaño del código o comprometer su legibilidad.
En general, creo que las primeras micro optimizaciones pueden ser una mala idea. Sin embargo, las macro optimizaciones (cosas como elegir un algoritmo O (log N) en lugar de O (N ^ 2)) a menudo valen la pena y deben hacerse temprano, ya que puede ser un desperdicio escribir un algoritmo O (N ^ 2) y luego bótelo completamente a favor de un enfoque O (log N).
Tenga en cuenta que las palabras pueden ser : si el algoritmo O (N ^ 2) es simple y fácil de escribir, puede tirarlo más tarde sin mucha culpa si resulta ser demasiado lento. Pero si ambos algoritmos son igualmente complejos, o si la carga de trabajo esperada es tan grande que ya sabe que necesitará el más rápido, la optimización temprana es una decisión de ingeniería sólida que reducirá su carga de trabajo total a largo plazo.
Por lo tanto, en general, creo que el enfoque correcto es averiguar cuáles son sus opciones antes de comenzar a escribir código y elegir conscientemente el mejor algoritmo para su situación. Lo más importante, la frase "la optimización prematura es la raíz de todo mal" no es excusa para la ignorancia. Los desarrolladores de carrera deberían tener una idea general de cuánto cuestan las operaciones comunes; deberían saber, por ejemplo,
Y los desarrolladores deben estar familiarizados con una caja de herramientas de estructuras de datos y algoritmos para que puedan usar fácilmente las herramientas adecuadas para el trabajo.
Tener muchos conocimientos y una caja de herramientas personal le permite optimizar casi sin esfuerzo. Poner mucho esfuerzo en una optimización que podría ser innecesaria es malo (y admito caer en esa trampa más de una vez). Pero cuando la optimización es tan fácil como elegir un conjunto / tabla hash en lugar de una matriz, o almacenar una lista de números en doble [] en lugar de cadena [], entonces ¿por qué no? Puede que no esté de acuerdo con Knuth aquí, no estoy seguro, pero creo que estaba hablando de optimización de bajo nivel, mientras que yo estoy hablando de optimización de alto nivel.
Recuerde, esa cita es originalmente de 1974. En 1974 las computadoras eran lentas y el poder de cómputo era costoso, lo que dio a algunos desarrolladores una tendencia a optimizar en exceso, línea por línea. Creo que contra eso estaba presionando Knuth. No decía "no te preocupes por el rendimiento en absoluto", porque en 1974 eso sería una locura. Knuth estaba explicando cómo optimizar; en resumen, uno debe enfocarse solo en los cuellos de botella, y antes de hacerlo debe realizar mediciones para encontrar los cuellos de botella.
Tenga en cuenta que no puede encontrar los cuellos de botella hasta que haya escrito un programa para medir, lo que significa que algunas decisiones de rendimiento deben tomarse antes de que exista algo para medir. A veces, estas decisiones son difíciles de cambiar si se equivocan. Por esta razón, es bueno tener una idea general de cuánto cuestan las cosas para que pueda tomar decisiones razonables cuando no hay datos disponibles.
Qué tan temprano para optimizar y cuánto preocuparse por el rendimiento dependen del trabajo. Al escribir scripts que solo ejecutará unas pocas veces, preocuparse por el rendimiento en general es una pérdida de tiempo completa. Pero si trabaja para Microsoft u Oracle y está trabajando en una biblioteca que miles de otros desarrolladores van a usar de miles de maneras diferentes, puede pagar para optimizar al máximo, de modo que pueda cubrir todos los diversos usar casos de manera eficiente. Aun así, la necesidad de rendimiento siempre debe equilibrarse con la necesidad de legibilidad, facilidad de mantenimiento, elegancia, extensibilidad, etc.
fuente
Personalmente, como se cubrió en un hilo anterior , no creo que la optimización temprana sea mala en situaciones en las que sabes que tendrás problemas de rendimiento. Por ejemplo, escribo software de análisis y modelado de superficies, donde trato regularmente con decenas de millones de entidades. La planificación del rendimiento óptimo en la etapa de diseño es muy superior a la optimización tardía de un diseño débil.
Otra cosa a tener en cuenta es cómo escalará su aplicación en el futuro. Si considera que su código tendrá una larga vida útil, también es una buena idea optimizar el rendimiento en la etapa de diseño.
En mi experiencia, la optimización tardía proporciona recompensas exiguas a un alto precio. La optimización en la etapa de diseño, a través de la selección de algoritmos y ajustes, es mucho mejor. Dependiendo de un generador de perfiles para comprender cómo funciona su código no es una excelente manera de obtener un código de alto rendimiento, debe saberlo de antemano.
fuente
De hecho, aprendí que la no optimización prematura es más a menudo la raíz de todo mal.
Cuando la gente escribe software, inicialmente tendrá problemas, como inestabilidad, funciones limitadas, mala usabilidad y mal rendimiento. Todo esto generalmente se arregla cuando el software madura.
Todo esto, excepto el rendimiento. A nadie parece importarle el rendimiento. La razón es simple: si un software falla, alguien corregirá el error y eso es todo, si falta una característica, alguien lo implementará y listo, si el software tiene un mal rendimiento, en muchos casos no se debe a la falta de microoptimización, pero debido al mal diseño y nadie va a tocar el diseño del software. SIEMPRE.
Mira a Bochs. Es lento como el infierno. ¿Alguna vez será más rápido? Tal vez, pero solo en el rango de un pequeño porcentaje. Nunca obtendrá un rendimiento comparable al software de virtualización como VMWare o VBox o incluso QEMU. ¡Porque es lento por diseño!
Si el problema de un software es que es lento, entonces porque es MUY lento y esto solo se puede solucionar mejorando el rendimiento por una multitud. + 10% simplemente no hará que un software lento sea rápido. Y generalmente no obtendrá más del 10% con optimizaciones posteriores.
Entonces, si el rendimiento es CUALQUIERA importante para su software, debe tenerlo en cuenta desde el principio, al diseñarlo, en lugar de pensar "oh sí, es lento, pero podemos mejorarlo más adelante". ¡Porque no puedes!
Sé que eso realmente no se ajusta a su caso específico, pero responde a la pregunta general "¿Es la optimización prematura realmente la raíz de todo mal?" - con un claro NO.
Cada optimización, como cualquier característica, etc., debe diseñarse e implementarse cuidadosamente. Y eso incluye una evaluación adecuada de costo y beneficio. No optimice un algoritmo para guardar algunos ciclos aquí y allá, cuando no cree una ganancia de rendimiento medible.
Solo como un ejemplo: puede mejorar el rendimiento de una función al incluirla, posiblemente ahorrando un puñado de ciclos, pero al mismo tiempo, probablemente aumente el tamaño de su ejecutable, aumentando las posibilidades de fallas de TLB y caché que cuestan miles de ciclos o incluso operaciones de paginación, que matarán el rendimiento por completo. Si no comprende estas cosas, su "optimización" puede resultar mala.
La optimización estúpida es más malvada que la optimización "prematura", pero ambas son aún mejores que la no optimización prematura.
fuente
Hay dos problemas con PO: en primer lugar, el tiempo de desarrollo que se usa para el trabajo no esencial, que podría usarse para escribir más funciones o corregir más errores, y en segundo lugar, la falsa sensación de seguridad de que el código se ejecuta de manera eficiente. PO a menudo implica optimizar el código que no va a ser el cuello de botella, sin darse cuenta del código que lo hará. El bit "prematuro" significa que la optimización se realiza antes de que se identifique un problema utilizando las medidas adecuadas.
Básicamente, sí, esto suena como una optimización prematura, pero no necesariamente lo retiraría a menos que presente errores, después de todo, ¡ahora se ha optimizado (!)
fuente
Creo que es lo que Mike Cohn llama 'chapado en oro' el código, es decir, pasar tiempo en cosas que podrían ser agradables pero que no son necesarias.
Él aconsejó contra eso.
PS 'Gold-chapado' podría ser una especie de funcionalidad de campanas y silbatos en cuanto a especificaciones. Cuando observa el código, toma la forma de una optimización innecesaria, clases 'preparadas para el futuro', etc.
fuente
Como no hay ningún problema para comprender el código, este caso podría considerarse como una excepción.
Pero, en general, la optimización conduce a un código menos legible y menos comprensible y debe aplicarse solo cuando sea necesario. Un ejemplo simple: si sabe que tiene que ordenar solo un par de elementos, utilice BubbleSort. Pero si sospecha que los elementos podrían aumentar y no sabe cuánto, entonces optimizar con QuickSort (por ejemplo) no es malo, sino imprescindible. Y esto debe considerarse durante el diseño del programa.
fuente
Descubrí que el problema con la optimización prematura ocurre principalmente cuando se reescribe el código existente para que sea más rápido. Puedo ver cómo podría ser un problema escribir una optimización enrevesada en primer lugar, pero sobre todo veo que la optimización prematura levanta su cabeza fea para arreglar lo que no está (se sabe que está) roto.
Y el peor ejemplo de esto es cuando veo que alguien vuelve a implementar características de una biblioteca estándar. Esa es una gran bandera roja. Como, una vez vi a alguien implementar rutinas personalizadas para la manipulación de cadenas porque le preocupaba que los comandos integrados fueran demasiado lentos.
Esto da como resultado un código que es más difícil de entender (malo) y gasta mucho tiempo en el trabajo que probablemente no sea útil (malo).
fuente
Desde una perspectiva diferente, según mi experiencia, la mayoría de los programadores / desarrolladores no planean el éxito y el "prototipo" casi siempre se convierte en la Versión 1.0. Tengo experiencia de primera mano con 4 productos originales separados en los que el front-end con clase, sexy y altamente funcional (básicamente la interfaz de usuario) resultó en una amplia adopción y entusiasmo por parte de los usuarios. En cada uno de estos productos, los problemas de rendimiento comenzaron a aparecer en tiempos relativamente cortos (1 a 2 años), particularmente a medida que los clientes más grandes y exigentes comenzaron a adoptar el producto. Muy pronto, el rendimiento dominó la lista de problemas, aunque el desarrollo de nuevas características dominó la lista de prioridades de la administración. Los clientes se sintieron cada vez más frustrados a medida que cada versión agregaba nuevas características que sonaban geniales pero eran casi inaccesibles debido a problemas de rendimiento.
Por lo tanto, los defectos de diseño e implementación muy fundamentales que eran de poca o ninguna preocupación en el "tipo prototipo" se convirtieron en los principales obstáculos para el éxito a largo plazo de los productos (y las empresas).
La demostración de su cliente puede verse y funcionar muy bien en su computadora portátil con XML DOM, SQL Express y muchos datos en caché del lado del cliente. El sistema de producción probablemente bloqueará una quemadura si tiene éxito.
En 1976 todavía estábamos debatiendo las formas óptimas de calcular una raíz cuadrada u ordenar una gran matriz y el adagio de Don Knuth se dirigió al error de enfocarse en optimizar ese tipo de rutina de bajo nivel al principio del proceso de diseño en lugar de enfocarse en resolver el problema y luego optimizando regiones localizadas de código.
Cuando uno repite el adagio como una excusa para no escribir código eficiente (C ++, VB, T-SQL o de otro tipo), o para no diseñar adecuadamente el almacén de datos, o para no considerar la arquitectura de la red, entonces IMO solo están demostrando un comprensión muy superficial de la naturaleza real de nuestro trabajo. Rayo
fuente
Supongo que depende de cómo se defina "prematuro". Hacer que la funcionalidad de bajo nivel sea rápida cuando estás escribiendo no es inherentemente malo. Creo que es un malentendido de la cita. A veces pienso que esa cita podría servir para algo más de calificación. Sin embargo, haría eco de los comentarios de m_pGladiator sobre la legibilidad.
fuente
La respuesta es, depende. Argumentaré que la eficiencia es un gran problema para ciertos tipos de trabajo, como las consultas complejas de bases de datos. En muchos otros casos, la computadora pasa la mayor parte del tiempo esperando la entrada del usuario, por lo que optimizar la mayoría del código es, en el mejor de los casos, una pérdida de esfuerzo y, en el peor de los casos, es contraproducente.
En algunos casos, puede diseñar por eficiencia o rendimiento (percibido o real), seleccionando un algoritmo apropiado o diseñando una interfaz de usuario para que ciertas operaciones costosas ocurran en segundo plano, por ejemplo. En muchos casos, la creación de perfiles u otras operaciones para determinar los puntos de acceso le brindarán un beneficio de 10/90.
Un ejemplo de esto que puedo describir es el modelo de datos que hice una vez para un sistema de gestión de casos judiciales que tenía alrededor de 560 tablas. Comenzó normalizado ('bellamente normalizado' como lo expresó el consultor de cierta firma de los 5 grandes) y solo tuvimos que agregar cuatro elementos de datos desnormalizados:
Una vista materializada para soportar una pantalla de búsqueda
Una tabla mantenida por disparador para admitir otra pantalla de búsqueda que no se pudo hacer con una vista materializada.
Una tabla de informes denormalizados (esto solo existía porque tuvimos que asumir algunos informes de rendimiento cuando un proyecto de almacén de datos se enlazó)
Una tabla mantenida por el disparador para una interfaz que tenía que buscar el más reciente de un gran número de eventos dispares dentro del sistema.
Este era (en ese momento) el proyecto J2EE más grande en Australasia, más de 100 años de tiempo de desarrollo, y tenía 4 elementos denormalizados en el esquema de la base de datos, uno de los cuales realmente no pertenecía allí en absoluto.
fuente
La optimización prematura no es la raíz de TODO el mal, eso es seguro. Sin embargo, hay inconvenientes:
En lugar de una optimización prematura, uno podría hacer pruebas de visibilidad tempranas para ver si existe una necesidad real de una mejor optimización.
fuente
La mayoría de los que se adhieren a "PMO" (la cita parcial, es decir) dicen que las optimizaciones deben basarse en mediciones y las mediciones no pueden realizarse hasta el final.
También es mi experiencia en el desarrollo de grandes sistemas que las pruebas de rendimiento se realizan al final, a medida que el desarrollo está por terminar.
Si siguiéramos los "consejos" de estas personas, todos los sistemas serían extremadamente lentos. También serían caros porque sus necesidades de hardware son mucho mayores de lo previsto originalmente.
Durante mucho tiempo he abogado por hacer pruebas de rendimiento a intervalos regulares en el proceso de desarrollo: indicará tanto la presencia de un nuevo código (donde anteriormente no había ninguno) como el estado del código existente.
Otra idea favorita es instrumentar el software en el nivel del bloque de funciones. A medida que el sistema se ejecuta, recopila información sobre los tiempos de ejecución de los bloques de funciones. Cuando se realiza una actualización del sistema, se puede determinar qué bloques de funciones funcionan como lo hicieron en la versión anterior y cuáles se han deteriorado. En la pantalla de un software, se puede acceder a los datos de rendimiento desde el menú de ayuda.
Echa un vistazo a esta excelente pieza sobre lo que PMO podría o no significar.
fuente