¿Cuándo la optimización no es prematura y, por lo tanto, no es mala?

75

"La optimización prematura es la raíz de todo mal" es algo que casi todos hemos escuchado / leído. Lo que tengo curiosidad es qué tipo de optimización no es prematura, es decir, en cada etapa del desarrollo de software (diseño de alto nivel, diseño detallado, implementación de alto nivel, implementación detallada, etc.), ¿qué grado de optimización podemos considerar sin que se cruce al lado oscuro?

Gaurav
fuente

Respuestas:

116

¿Cuándo lo estás basando en la experiencia? No es malvado "Cada vez que hacemos X, sufrimos un golpe brutal en el rendimiento. Planifiquemos optimizar o evitar X por completo esta vez".

Cuando es relativamente indoloro? No es malvado "Implementar esto como Foo o Bar requerirá tanto trabajo, pero en teoría, Bar debería ser mucho más eficiente. Barremos".

¿Cuándo evitas algoritmos de mierda que se escalarán terriblemente? No es malvado "Nuestro líder técnico dice que nuestro algoritmo de selección de ruta propuesto se ejecuta en tiempo factorial; no estoy seguro de lo que eso significa, pero ella sugiere que cometamos seppuku incluso por considerarlo. Consideremos otra cosa".

El mal proviene de pasar mucho tiempo y energía para resolver problemas que no sabes que realmente existen. Cuando los problemas definitivamente existen, o cuando los psudoproblemas fantasmas pueden resolverse a bajo precio, el mal desaparece.


Steve314 y Matthieu M. plantean puntos en los comentarios que deberían considerarse. Básicamente, algunas variedades de optimizaciones "indoloras" simplemente no valen la pena, ya que la mejora de rendimiento trivial que ofrecen no vale la ofuscación del código, están duplicando las mejoras que el compilador ya está realizando, o ambas cosas. Vea los comentarios para ver algunos ejemplos agradables de no mejoras demasiado inteligentes a la mitad.

BlairHippo
fuente
22
Ocasionalmente, resolver un problema fantasma fácilmente sigue siendo levemente malo, ya que puede resultar en un código más difícil de leer y más difícil de mantener. No es mucho más difícil (o no sería una solución fácil), pero quizás de vez en cuando sigue siendo relevante. Un ejemplo podría ser el uso de un ingenioso truco bit a bit que algunas personas no reconocerán y que el compilador probablemente aplicará de todos modos si es útil.
Steve314
26
Estoy de acuerdo con Steve aquí, a veces la "optimización" simplemente no vale la pena, especialmente porque los compiladores son tan buenos. Ejemplo? si ino está firmado, i / 2se puede reemplazar por i >> 1. Es mas rapido. Pero también es más críptico (no todos verán el efecto, incluso aquellos que lo hagan pueden perder tiempo). Pero lo peor de todo es que el compilador lo hará de todos modos, entonces, ¿por qué ofuscar el código fuente;)?
Matthieu M.
19
@Larry: No lo hice, así que supongo que es un buen ejemplo.
Joris Meys
18
En mi opinión, las optimizaciones, incluso las simples, también deben considerarse malvadas si afectan la legibilidad / mantenimiento del código y no se basan en mediciones de rendimiento reales.
Bart van Ingen Schenau
14
@Matthew: ¿Enseñarles qué? ¿Trucos sucios e innecesarios? ¿Por qué? Si el perfil muestra que a i/2es realmente un punto caliente y eso (increíble, pero supongamos) lo i>>1hace más rápido, hágalo, y póngale un comentario que este perfil mostró que es más rápido. Si esto es realmente necesario en cualquier lugar (lo cual dudo, ya que, como dijo Matthieu, los compiladores deberían ser lo suficientemente inteligentes como para hacerlo ellos mismos), los novatos aprenderán algo, si no lo es (lo que es probable), ¿por qué quieres conectarlo? sus cabezas con folklore innecesario?
sbi
38

El código de la aplicación solo debe ser tan bueno como sea necesario, pero el código de la biblioteca debe ser lo mejor posible, ya que nunca se sabe cómo se va a utilizar su biblioteca. Entonces, cuando escribe código de biblioteca, debe ser bueno en todos los aspectos, ya sea rendimiento, solidez o cualquier otra categoría.

Además, debe pensar en el rendimiento al diseñar su aplicación y al elegir algoritmos . Si no está diseñado para ser eficaz, ningún grado de piratería informática puede ser eficaz después y ninguna micro-optimización superará a un algoritmo superior.

sbi
fuente
55
El código de la biblioteca debe documentar si está tratando de ser "lo mejor posible" o cuál es su objetivo. El código no necesita ser absolutamente óptimo para ser útil, siempre que los consumidores solo lo usen cuando sea apropiado.
supercat
1
Lo sentimos, pero "ser bueno en todos los aspectos" suena sospechosamente como un exceso de ingeniería. Además, probablemente no sea realista: la vida siempre se trata de compensaciones.
sleske
1
+1 para enfatizar la fase de diseño; Si sopesas deliberadamente sus beneficios, no es prematuro.
Nathan Tuggy
Por el contrario, si nunca sabe cómo se usará su biblioteca, no sabe si dedicar tiempo a mejorarla tiene algún valor comercial. Entonces eso no es un argumento.
RemcoGerlich
25

qué tipo de optimización [no] es prematura

Del tipo que viene como resultado de problemas conocidos.

George Marian
fuente
17

¿Cuándo la optimización no es prematura y, por lo tanto, no es mala?

Es difícil decir qué es bueno y malo. ¿Quién tiene ese derecho? Si observamos la naturaleza, parece que estamos programados para la supervivencia con una definición amplia de "supervivencia" que incluye transmitir nuestros genes a la descendencia.

Entonces, diría, al menos de acuerdo con nuestras funciones básicas y programación, que la optimización no es mala cuando se alinea con el objetivo de la reproducción. Para los chicos, están las rubias, morenas, pelirrojas, muchas encantadoras. Para las chicas, hay chicos, y algunos de ellos parecen estar bien.

Quizás deberíamos estar optimizando para ese propósito, y allí ayuda usar un generador de perfiles. El generador de perfiles le permitirá priorizar sus optimizaciones y el tiempo de manera más efectiva además de brindarle información detallada sobre los puntos de acceso y por qué ocurren. Esto le dará más tiempo libre dedicado a la reproducción y su búsqueda.


fuente
3
Es refrescante ver a alguien traer una nueva versión de este viejo castaño. Todo lo que se necesita es leer la cita completa de Knuth, y no solo una oración, ¿eh?
Robert Harvey
1
@RobertHarvey Me molesta un poco, ya que muchos parecen citar solo una frase y tanta información contextual importante termina perdiéndose en el proceso. No estoy seguro de que sea una gran respuesta, ya que recibí un poco. :-D
14

La cita completa define cuándo la optimización no es prematura:

Un buen programador no se dejará llevar por la complacencia por tal razonamiento, será sabio al mirar cuidadosamente el código crítico; pero solo después de que se haya identificado ese código . [énfasis mío]

Puede identificar el código crítico de muchas maneras: las estructuras o algoritmos de datos críticos (por ejemplo, utilizados en gran medida o el "núcleo" del proyecto) pueden proporcionar optimizaciones importantes, muchas optimizaciones menores se identifican a través de los perfiladores, etc.

Fred Nurk
fuente
66
Sí ... Está bien recortar el 90% del tiempo que tarda una llamada de función aleatoria, pero tal vez obtendría un mayor impacto mirando el código donde su aplicación realmente pasa el 80% de su tiempo y recortando un Un pequeño porcentaje allí.
11

Siempre debe elegir una solución "suficientemente buena" en todos los casos según sus experiencias.

El dicho de optimización se refiere a escribir "código más complejo que 'lo suficientemente bueno' para hacerlo más rápido" antes de saber que es necesario, por lo que el código es más complejo de lo necesario. La complejidad es lo que dificulta las cosas, por lo que no es algo bueno.

Esto significa que no debe elegir una rutina de clasificación súper compleja "puede ordenar archivos de 100 Gb intercambiando de manera transparente al disco" cuando lo haga una ordenación simple, pero también debe hacer una buena elección para la ordenación simple en primer lugar. Elegir ciegamente Bubble Sort o "seleccionar todas las entradas al azar y ver si están en orden. Repita". Raramente es bueno.


fuente
3

Mi regla general: si no está seguro de que necesitará la optimización, suponga que no. Pero tenlo en cuenta para cuando necesites optimizar. Sin embargo, hay algunos problemas que puede conocer por adelantado. Esto generalmente implica elegir buenos algoritmos y estructuras de datos. Por ejemplo, si necesita verificar la membresía en una colección, puede estar bastante seguro de que necesitará algún tipo de estructura de datos establecida.

Jason Baker
fuente
3

En mi experiencia, en la fase de implementación detallada, la respuesta está en el perfil del código. Es importante saber qué necesita ser más rápido y qué es aceptablemente rápido.

También es importante saber dónde se encuentra exactamente el cuello de botella en el rendimiento: la optimización de una parte del código que toma solo el 5% del tiempo total para ejecutarse no sirve de nada.

Los pasos 2 y 3 describen la optimización no prematura:

  1. Hazlo funcionar
  2. Prueba. ¿No suficientemente rápido? Perfílalo .
  3. Usando los datos del paso 2, optimice las secciones más lentas del código.
Gorgi Kosev
fuente
Olvidó el paso 0, que es: diseñar la aplicación correctamente para que pueda esperar un rendimiento razonable desde el principio.
Robert Harvey
Solo estaba hablando sobre la fase de implementación detallada.
Gorgi Kosev
1
Cuestiono el paso 3: muy a menudo, la mejor respuesta es encontrar un enfoque diferente para no estar haciendo el código lento en primer lugar.
Loren Pechtel
1
Elija las estructuras de datos correctas.
jasonk
3

No es optimización al elegir cosas que son difíciles de cambiar, por ejemplo: plataforma de hardware.

Elegir estructuras de datos es un buen ejemplo, crítico para cumplir con los requisitos funcionales y no funcionales (rendimiento). No se cambia fácilmente y, sin embargo, controlará todo lo demás en su aplicación. Sus estructuras de datos cambian los algoritmos disponibles, etc.

jasonk
fuente
3

Solo conozco una forma de responder a esta pregunta, y es obtener experiencia en el ajuste del rendimiento. Eso significa: escribir programas, y después de que se escriben, encontrar aceleraciones en ellos y hacerlo de forma iterativa. Aquí hay un ejemplo.

Este es el error que comete la mayoría de las personas: intentan optimizar el programa antes de ejecutarlo. Si han tomado un curso de programación (de un profesor que en realidad no tiene mucha experiencia práctica) tendrán anteojos grandes de color O, y pensarán que de eso se trata . Es todo el mismo problema, optimización previa. **

Alguien dijo: Primero hazlo bien, luego hazlo rápido. Tenían razón

Pero ahora para el pateador: si has hecho esto varias veces, reconoces las cosas tontas que hiciste antes que causan problemas de velocidad, por lo que las evitas instintivamente. (Cosas como hacer que la estructura de tu clase sea demasiado pesada, inundarte de notificaciones, confundir el tamaño de las llamadas de función con su costo de tiempo, la lista sigue y sigue ...) Instintivamente las evitas, pero adivina cómo se ve al menos. experimentado: optimización prematura!

Así que estos debates tontos siguen y siguen :)

** Otra cosa que dicen es que ya no tienes que preocuparte por eso, porque los compiladores son muy buenos y las máquinas son tan rápidas hoy en día. (KIWI - Kill It With Iron.) No hay aceleraciones exponenciales del hardware o del sistema (realizadas por ingenieros muy inteligentes y trabajadores) que posiblemente puedan compensar la desaceleración exponencial del software (realizada por programadores que piensan de esta manera).

Mike Dunlavey
fuente
2

Cuando los requisitos o el mercado lo solicite específicamente.

Por ejemplo, el rendimiento es un requisito en la mayoría de las aplicaciones financieras porque la baja latencia es crucial. Dependiendo de la naturaleza del instrumento comercializado, la optimización puede ir desde el uso de algoritmos sin bloqueo en un lenguaje de alto nivel hasta el uso de un lenguaje de bajo nivel e incluso el extremo: implementar los algoritmos de coincidencia de órdenes en el hardware mismo (usando FPGA por ejemplo )

Otro ejemplo sería algunos tipos de dispositivos integrados. Tomemos por ejemplo el freno ABS; en primer lugar está la seguridad, cuando golpeas el freno, el auto debe disminuir la velocidad. Pero también hay rendimiento, no querrás demoras cuando llegues al descanso.

m3th0dman
fuente
0

La mayoría de las personas llamaría a la optimización prematura, si está optimizando algo que no está dando como resultado un "fallo suave" (funciona pero sigue siendo inútil) del sistema debido al rendimiento.

Ejemplos del mundo real.

  • Si mi ordenamiento de burbujas tarda 20 ms en ejecutarse, optimizarlo a 1 ms de clasificación rápida no mejorará la utilidad general de ninguna manera significativa a pesar de ser un aumento del rendimiento del 2000%.

  • Si una página web tarda 20 segundos en cargarse y la reducimos a 1 segundo, esto puede aumentar la utilidad del sitio web de 0 a casi infinito. Básicamente, algo que se rompió porque era demasiado lento, ahora es útil.

Eric
fuente
Es importante tener en cuenta que si su clasificación se llama 1000 veces en el transcurso de su programa, la optimización de 20 ms a 1 ms es un gran problema.
Beefster
0

¿Qué tipo de optimización no es prematura?

Una optimización que corrige un problema de rendimiento conocido con su aplicación, o una optimización que permite que su aplicación cumpla con criterios de aceptación bien definidos.

Una vez identificados, se debe tomar un tiempo para establecer la solución y se debe medir el beneficio de rendimiento.

(es decir, no lo es: "Creo que esta parte del código parece que podría ser lenta, cambiaré X para usar Y y eso será más rápido").

Me he encontrado con una gran cantidad de "optimización" prematura que finalmente ha hecho que el código sea más lento; en este caso, estoy tomando prematuro para significar "no pensado". El rendimiento debe ser comparado antes y después de la optimización y solo se debe mantener un código que realmente mejore el rendimiento.

Arrozal
fuente
0

"La optimización prematura es la raíz de todo mal" es algo que casi todos hemos escuchado / leído

Cierto. Desafortunadamente, también es una de las citas de programación más mal utilizadas (maliciosamente) de todos los tiempos. Como Donald Knuth acuñó el meme, vale la pena agregar un contexto original de la cita:

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. Sin embargo, no debemos dejar pasar nuestras oportunidades en ese crítico 3%. ... Un buen programador ... será prudente mirar cuidadosamente el código crítico; pero solo después de que se haya identificado ese código. ... la experiencia universal de los programadores que han estado utilizando herramientas de medición ha sido que sus conjeturas intuitivas fallan

Tenga en cuenta que Knuth habló específicamente sobre la velocidad de ejecución en tiempo de ejecución .

Los programadores pierden enormes cantidades de tiempo pensando o preocupándose por la velocidad de las partes no críticas de sus programas.

Además, escribió el artículo en 1974 cuando cualquier recurso de máquina donde la correlación premium y negativa entre la velocidad de ejecución y la capacidad de mantenimiento del programa (mayor velocidad - menos mantenible) probablemente era más fuerte que ahora.

OK, para responder a su pregunta, según Donald Knuth, la optimización NO es prematura si soluciona un serio cuello de botella de rendimiento que se ha identificado (idealmente medido y señalado durante el perfilado).

Como dije antes, la "optimización prematura" es uno de los memes más mal utilizados, por lo que la respuesta no estará completa sin algunos ejemplos de cosas que no son optimizaciones prematuras pero que a veces se ignoran como tales:

  • cuellos de botella que son visibles a simple vista y que pueden evitarse antes de ser introducidos, como el número O (N ^ 2) de viajes de ida y vuelta a la base de datos con N grande donde existe la alternativa O (1)

Además, ni siquiera están relacionados con la velocidad de ejecución del tiempo de ejecución:

Kola
fuente