He estado programando durante un par de años y a menudo me encuentro en un dilema.
Hay dos soluciones:
- uno es simple, es decir, un enfoque simple, más fácil de entender y mantener. Implica algo de redundancia, algo de trabajo adicional (IO adicional, procesamiento adicional) y, por lo tanto, no es la solución más óptima.
- pero otro utiliza un enfoque complejo, difícil de implementar, que a menudo involucra la interacción entre muchos módulos y es una solución eficiente de rendimiento.
¿Qué solución debo buscar cuando no tengo un SLA de rendimiento difícil de cumplir e incluso la solución simple puede cumplir con el SLA de rendimiento? He sentido desdén entre mis colegas desarrolladores por una solución simple.
¿Es una buena práctica encontrar la solución compleja más óptima si su SLA de rendimiento se puede cumplir con una solución simple?
design
programming-practices
coding-style
code-quality
performance
Muévete rápido
fuente
fuente
Respuestas:
El simple Cumple con las especificaciones, es más fácil de entender, es más fácil de mantener y probablemente sea mucho menos problemático.
Lo que está haciendo para abogar por la solución eficiente de rendimiento es introducir generalidad especulativa y optimización prematura en su código. ¡No lo hagas! El rendimiento va en contra de casi cualquier otra 'ilidad' de ingeniería de software que existe (confiabilidad, mantenibilidad, legibilidad, comprobabilidad, comprensibilidad, ...). Perseguir el rendimiento cuando se realizan pruebas indica que realmente es necesario perseguir el rendimiento.
No persiga el rendimiento cuando el rendimiento no importa. Incluso si es importante, solo debe perseguir el rendimiento en aquellas áreas donde las pruebas indican que existe un cuello de botella en el rendimiento. No permita que los problemas de rendimiento sean una excusa para reemplazar
simple_but_slow_method_to_do_X()
con una versión más rápida si esa versión simple no aparece como un cuello de botella.El rendimiento mejorado casi inevitablemente se ve obstaculizado por una gran cantidad de problemas de olor a código. Usted ha mencionado varios en la pregunta: un enfoque complejo, difícil de implementar, mayor acoplamiento. ¿Realmente vale la pena arrastrarlos?
fuente
Respuesta corta: prefiera las soluciones simples a las complejas y recuerde los principios de KISS y YAGNI
Debido a que los requisitos iniciales del proyecto y el software nunca son perfectos, requiere cambios a medida que se desarrolla / usa la aplicación. El enfoque iterativo en las fases de desarrollo es una muy buena combinación para comenzar cosas simples y extenderlas según sea necesario. Las soluciones más simples tienen espacio para la flexibilidad y son más fáciles de mantener.
Además, tratar de ser inteligente y colocar algo de optimización adicional mientras construye su aplicación no es una buena práctica y puede complicar demasiado su solución. Como se sabe,
"premature optimization is the root of all evil"
del libro de Knuthfuente
Tome una lección de Knuth aquí: "Deberíamos olvidarnos de las pequeñas eficiencias, digamos alrededor del 97% del tiempo: la optimización prematura es la raíz de todo mal".
Piense en sus soluciones en este orden: Primero, siempre, lo correcto. En segundo lugar, mejorar la claridad y la simplicidad. Tercero, y solo cuando puede demostrar la necesidad, la eficiencia.
Agregar eficiencia casi siempre le costará algo importante, por lo que solo debe buscarse cuando sepa que lo necesita.
fuente
La simplicidad es un requisito previo de fiabilidad . Si tiene una solución simple que funciona, ¡anímate! Es mucho más fácil optimizar un programa de trabajo que hacer que un programa optimizado funcione. Además, no se olvide de la ley de Moore : si su solución simple cumple con los objetivos de rendimiento hoy, probablemente los aplastará 1 en uno o dos años.
1 No hay garantía allí, porque como Jimmy Hoffa señaló en su comentario a continuación, la ley de Moore tiene sus límites.
fuente
¡Óptimo es una palabra ambigua!
En última instancia, si hay mucho riesgo en tener que mantener el complejo, y si el simple es "suficientemente bueno", siempre me equivocaría del lado del simple.
Agregue cualquier riesgo de que el complejo no sea lo suficientemente bueno, entonces KISS es probablemente la respuesta correcta.
fuente
Prefiero el sencillo. En mi opinión, las optimizaciones prematuras causan tantos problemas como se resuelven. En muchos casos, un buen diseño le permite cambiar implementaciones dadas en el futuro, si se convierten en cuellos de botella.
En resumen, lo diseñaré lo más flexible posible, pero no sacrificaré demasiado la simplicidad por la flexibilidad.
fuente
¿Cuál cuesta menos?
La mayoría de las veces, una solución simple que sea un poco más lenta será perfectamente aceptable en términos de rendimiento, y la simplicidad la hace más económica de desarrollar, mantener y eventualmente reemplazar.
Por otro lado, a veces la velocidad es realmente importante, y la ganancia financiera que proviene incluso de pequeñas mejoras de velocidad puede ser mucho mayor que el aumento del costo de una solución más complicada. Por ejemplo, reducir 0.01s el tiempo para completar una transacción puede hacer que un sistema de negociación de valores sea mucho más rentable. Una mejora del 10% en la eficiencia de un sistema que admite varios millones de usuarios podría significar una reducción significativa en los costos del servidor.
Entonces, la pregunta que debe hacerse es: ¿el uso de la solución compleja tiene suficiente impacto en el resultado final para pagar su costo adicional? En realidad, probablemente debería pedirle a su cliente que decida ya que está pagando las facturas y cosechando los beneficios potenciales. Una buena opción es ir primero con la solución simple y ofrecer la solución más compleja como una posible mejora. Eso le permite poner en funcionamiento su sistema y le da a su cliente algo para comenzar a probar, y esa experiencia puede informar la decisión de implementar (o no implementar) la solución más complicada.
fuente
Al evaluar dos enfoques, uno más simple pero menos eficiente, mientras que otro es más complejo y más eficiente, uno tiene que considerar el problema y el dominio del proyecto.
Considere un proyecto de software multimillonario para la industria de la salud que ha planificado una vida útil de más de 15 años de mantenimiento y más de 20 años de uso. En tal proyecto, el rendimiento definitivamente no será una preocupación, pero la complejidad y la estructura del proyecto pueden causar problemas importantes para el mantenimiento del proyecto, que dura un mínimo de 15 años. La capacidad de mantenimiento y la simplicidad están antes que nada.
Entonces, considere otro ejemplo. Un motor de juegos de consola que se supone que impulsará los próximos juegos de la compañía durante los próximos 5 años. Debido a que los juegos son programas extremadamente limitados en recursos, la eficiencia va antes que la mantenibilidad en muchos casos. Escribir sus propias estructuras de datos y algoritmos muy específicos para alguna tarea puede ser muy importante incluso si va en contra de cualquier tipo de "mejores prácticas" de desarrollo de software. Un buen ejemplo de esto podría ser el diseño orientado a datos en el que almacena sus datos en matrices de datos similares, en lugar de en objetos reales. Esto es para aumentar la referencia de la localidad y, como tal, aumentar la eficiencia de la memoria caché de la CPU. No práctico, pero muy crucial en el dominio dado.
fuente
Esta es siempre una pregunta difícil y veo que las respuestas oscilan en un sentido, así que jugaré el juego para el otro lado, aunque no afirmo que ninguna de las respuestas sea correcta, es un tema muy suave y caso por caso.
Una cosa acerca de una solución compleja pero de alto rendimiento es que siempre puede documentar todo lo que pueda. En general, soy fanático del código autodocumentado, pero también soy fanático del software que responde en una cantidad de tiempo que me hace sentir que no me está frenando. Si opta por la solución compleja pero de alto rendimiento, considere lo que puede hacer para que no sea tan mala:
Envuélvalo en una interfaz, colóquelo en un ensamblaje por sí mismo, posiblemente incluso en un proceso propio. Hágalo lo más flojo posible con una pared de abstracción lo más gruesa posible para evitar fugas . Escriba muchas pruebas unitarias para guardar regresiones en el futuro.
Documente en el código, incluso considere escribir alguna documentación real. Piense en estructuras de datos complejas y cómo están documentadas, imagine tratar de comprender la implementación de una de ellas desde el código sin un libro de estructuras de datos / artículo de Wikipedia para explicarlo. Y, sin embargo, todos aceptamos que estas estructuras de datos complejas son, de hecho, buenas y es beneficioso que alguien las haya implementado en nuestros idiomas.
Recuerde que todos estamos enviando mensajes en una pila TCP / IP que probablemente sea tan difícil como el código puede obtener si alguno de nosotros lo mirara, expresamente para que funcione de la manera en que todos lo necesitamos también. Tal vez su problema no requiera este nivel de optimización, tal vez sí, pero tenga cuidado al abordar esta pregunta, ya que todos tenemos que hacerlo de vez en cuando: hay dragones allí.
fuente
Estoy llegando a esto trabajando en áreas donde no hay SLA de rendimiento. Cuando se trata de renderizadores fuera de línea en gráficos de computadora, no existe un "rendimiento satisfactorio" para los usuarios, porque ya están distribuyendo enormes sumas de dinero para distribuir la computación a través de las nubes y renderizar granjas incluso con los renderizadores de última generación. para producir imágenes y fotogramas de calidad de producción para películas, p. ej.
Pero tengo que decir, como alguien que trabaja en este dominio durante muchos años, que cualquier solución que degrada significativamente la capacidad de mantenimiento en favor de la eficiencia en realidad está trabajando en contra de los requisitos de rendimiento siempre cambiantes. Porque si no puede mantener eficazmente su solución en los próximos años, ya que las cosas están cambiando bajo sus pies (tanto en términos de código circundante como de lo que los usuarios esperan que los competidores sigan superando entre sí), entonces su solución ya está trabajando hacia la obsolescencia y necesidad de reemplazo al por mayor.
No veo el propósito final de los perfiladores como VTune como una forma de hacer que mi código se ejecute más rápido. Su valor final es asegurarse de que no esté degradando mi productividad para satisfacer las demandas de rendimiento cada vez mayores. Si tengo que aplicar alguna microoptimización de aspecto grosero, entonces el perfilador, combinado con ejecutarlo en casos de usuarios reales (y no en algún caso de prueba que imagino que podría ser importante), se asegura de aplicar ese aspecto inevitablemente grosero optimizaciones muy, muy juiciosas a solo los principales puntos de acceso que aparecen, así como documentarlos con mucho cuidado porque inevitablemente tendré que volver a visitarlos, mantenerlos, ajustarlos y cambiarlos en los próximos años si esa solución sigue siendo viable.
Y especialmente si su solución optimizada implica más acoplamiento, entonces realmente sería reacio a usarla. Una de las métricas más valiosas que he llegado a apreciar en las áreas más críticas para el rendimiento de la base de código es el desacoplamiento (como minimizar la cantidad de información que algo necesita para funcionar, lo que también minimiza la probabilidad de que requiera cambios a menos que los necesite directamente). ), porque esas áreas críticas multiplican significativamente las razones para que las cosas cambien. Lo que significa que cuanto menos información requiera algo para trabajar, menos razones tiene para cambiar, y minimizar las razones para el cambio es realmente una gran parte de mejorar la productividad en mis áreas particulares de enfoque porque las cosas tendrán que cambiar constantemente de todos modos (nosotros en otro año quedará obsoleto)
Para mí, las soluciones más grandes y efectivas que he encontrado son aquellas en las que la eficiencia, la mantenibilidad y la productividad no son diametralmente opuestas entre sí. La búsqueda para mí es tratar de hacer que estos conceptos sean lo más armoniosos posible.
fuente