Solución simple versus compleja (pero eficiente en el rendimiento): ¿cuál elegir y cuándo?

28

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?

Muévete rápido
fuente
10
ver: ¿Cómo evito la "intuición de mala optimización del desarrollador"? "Desafortunadamente, los desarrolladores generalmente tienen una intuición horrible sobre dónde estarán realmente los problemas de rendimiento en una aplicación ..."
mosquito
1
¿Será "no el más óptimo" aún "suficientemente bueno"? Entonces quédate con eso.
8
Sí. " Le mieux est l'ennemi du bien ", Voltaire. ("Perfecto es enemigo de lo bueno"). Lo suficientemente bueno es lo suficientemente bueno, hasta que las pruebas de rendimiento indiquen lo contrario.
David Hammen
Encuentro que (en general) simple implica eficiente. Por lo tanto, a menudo no es necesario comprometerse.
Dan
2
"Parece que la perfección se alcanza no cuando no hay nada más que agregar, sino cuando no hay nada más que eliminar". - Antoine de Saint-Exupery
keuleJ

Respuestas:

58

¿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?

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?

David Hammen
fuente
su respuesta es muy útil
MoveFast
1
Tan simple como sea posible, pero no más simple; Lo más rápido posible, pero no más rápido; etc
user606723
2
"En caso de duda, use la fuerza bruta";)
tdammers
Los comentarios en el código pueden ser tanto catárticos como útiles aquí. Un pequeño comentario que señala la solución compleja + rápida, y por qué no la usaste, puede hacerte sentir que ignoraste el algoritmo óptimo. Y puede ayudar a los encargados de mantenimiento a comprender su elección y orientarlos en la dirección correcta si la optimización se necesita más adelante.
TheAtomicOption
12

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 Knuth

EL Yusubov
fuente
1
@ManojGumber, no hay problema y es realmente la esencia de lo que nosotros, como programadores, deberíamos preocuparnos en primer lugar.
EL Yusubov
8

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.

simon
fuente
44
Tenga en cuenta que esto no significa que no deba escribir una buena implementación en primer lugar.
@ ThorbjørnRavnAndersen: por supuesto, de eso se tratan los dos primeros puntos.
simon
1
@simon la cita se usa con frecuencia como una excusa para elegir descuidadamente,
Sobre su segundo punto: tuve un colega que a menudo decía que prefería códigos incorrectos, bien estructurados y limpios antes que los espaguetis correctos.
Buhb
@ ThorbjørnRavnAndersen personas incompetentes usarán cualquier cosa como excusa. No tiene ningún impacto en el valor del pensamiento original.
Simon
7

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.

dasblinkenlight
fuente
Te olvidaste de la otra ley de Moore que dice: "Vaya, de mi primera ley ..." Lo siento jefe, la ley de Moore ya no existe (juego de palabras). No estoy en desacuerdo con el resto de su punto, solo al menos advertiría esa última parte allí.
Jimmy Hoffa
2
Lo siento, pero en toda mi experiencia en esta industria. Los "conjuntos de trabajo" están aumentando MUCHO más rápido que la velocidad de nuestro hardware, que se actualiza constantemente. Realmente, simplemente eliminaría el punto de la ley de Moore.
user606723
@ user606723 El crecimiento del punto de "conjunto de trabajo" es ortogonal a la pregunta "optimizada o simple": la carga de trabajo los alcanzará independientemente de la solución que implementen. El punto de incorporar la ley de Moore a la mezcla fue señalar que incluso si la solución simple está bajo cierta presión de rendimiento al momento de escribir, la presión disminuirá a medida que el hardware esté más rápido disponible.
dasblinkenlight
@dasblinkenlight, el crecimiento del conjunto de trabajo no es más ortogonal a la pregunta que la ley de Moore. El punto de traer el problema al entorno de trabajo es que si una solución simple está bajo cierta presión de rendimiento en el momento del lanzamiento, el rendimiento será insuficiente en un futuro muy cercano, ya que el aumento del trabajo demole cualquier mejora de rendimiento lograda por un hardware mejorado. Si bien estoy a favor de un software simple, confiable y fácil de mantener, lanzar un software que ya está bajo presión de rendimiento en el lanzamiento y esperar que la ley de Moore lo iguale es una filosofía terrible.
user606723
3

¿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?

¡Ó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.

Andrés
fuente
2

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.

Tsvetomir Dimitrov
fuente
2

¿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.

Caleb
fuente
2

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.

zxcdw
fuente
1

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í.

Jimmy Hoffa
fuente
0

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.

Dragon Energy
fuente