¿Cuándo es prematura la optimización?


82

Como dijo Knuth,

Deberíamos olvidarnos de las pequeñas eficiencias, digamos alrededor del 97% del tiempo: la optimización prematura es la raíz de todos los males.

Esto es algo que a menudo surge en las respuestas de Stack Overflow a preguntas como "¿cuál es el mecanismo de bucle más eficiente", "técnicas de optimización de SQL?" ( y así sucesivamente ). La respuesta estándar a estas preguntas de consejos de optimización es perfilar su código y ver si es un problema primero, y si no lo es, entonces su nueva técnica no es necesaria.

Mi pregunta es, si una técnica en particular es diferente pero no particularmente oscura u ofuscada, ¿se puede realmente considerar una optimización prematura?

Aquí hay un artículo relacionado de Randall Hyde llamado La falacia de la optimización prematura .


41
Es un poco irónico que muchas personas que gritar "optimización prematura es la raíz de todo mal" a sí mismos antes de tiempo se han optimizado la cita: (cont)
algunos

21
"Debemos olvidarnos de pequeñas eficiencias, por ejemplo alrededor del 97% del tiempo: la optimización prematura es la raíz de todos los males Sin embargo, no hay que dejar pasar las oportunidades en que crítica 3%." (Donald Knuth)
algunos

2
Creo que fue CA Hoare quien dijo esto. Incluso Knuth lo dice.
jamesh

1
sí, Tony Hoare dijo por primera vez que "la optimización prematura es la raíz de todas las partes malas", pero Knuth lo citó / parafraseó agregando el resto, creo
nickf

2
Si bien estoy de acuerdo en que la cita es una pregunta que a menudo se abusa y se saca de contexto, por definición siempre es correcta debido a lo "prematuro" (sin embargo, la mayoría de las veces se usa incorrectamente como justificación de un diseño y código descuidados). Por definición, si la optimización ocurrió en el momento más oportuno del desarrollo, ya sea durante el diseño o en cualquier otro momento, no fue "prematuro".
Lawrence Dol

Respuestas:


103

Don Knuth inició el movimiento de la programación alfabetizada porque creía que la función más importante del código de computadora es comunicar la intención del programador a un lector humano . Cualquier práctica de codificación que haga que su código sea más difícil de entender en nombre del rendimiento es una optimización prematura.

Ciertos modismos que se introdujeron en nombre de la optimización se han vuelto tan populares que todos los entienden y se han vuelto esperados , no prematuros. Ejemplos incluyen

  • Usar aritmética de punteros en lugar de notación de matriz en C, incluido el uso de expresiones idiomáticas como

    for (p = q; p < lim; p++)
    
  • Volver a vincular variables globales a variables locales en Lua, como en

    local table, io, string, math
        = table, io, string, math
    

Más allá de esos modismos, tome atajos bajo su propio riesgo .

Toda optimización es prematura a menos que

  • Un programa es demasiado lento (muchas personas olvidan esta parte).

  • Tiene una medición (perfil o similar) que muestra que la optimización podría mejorar las cosas .

(También está permitido optimizar la memoria).

Respuesta directa a la pregunta:

  • Si su técnica "diferente" hace que el programa sea más difícil de entender , entonces es una optimización prematura .

EDITAR : En respuesta a los comentarios, el uso de ordenación rápida en lugar de un algoritmo más simple como la ordenación por inserción es otro ejemplo de un modismo que todos entienden y esperan . (Aunque si escribe su propia rutina de clasificación en lugar de utilizar la rutina de clasificación de la biblioteca, es de esperar que tenga una muy buena razón).


13
Por sus definiciones; Si una implementación de clasificación rápida es más difícil de leer y comprender que una clasificación de burbujas, es una optimización prematura. ¿No puedes optimizar para la memoria? Intente buscar los mismos ejemplos para matrices dispersas grandes. En mi humilde opinión, la mayor parte de la optimización debería ocurrir en la etapa de diseño. yo, e, muy temprano.
SmacL

1
@frankodwyer: pero incrementar el puntero es posiblemente más rápido que incrementar un contador y usar la notación de matriz, y sería una optimización prematura.
Joachim Sauer

5
@Norman: Aunque quicksort ahora es omnipresente, no lo fue cuando se inventó por primera vez y, por lo tanto, QED fue una optimización prematura con la que el autor no tenía nada que hacer, ¿verdad?
Lawrence Dol

5
@Software Monkey: Absolutamente. Toda la investigación de CS es un desperdicio del dinero de los contribuyentes y debe detenerse de inmediato.
Norman Ramsey

11
Cualquier algoritmo de clasificación, incluido el que usted inventó, es claro y conciso si se escribe como una función separada llamada sortQuickly (...) con los comentarios adecuados.
ilya n.

40

En mi humilde opinión, el 90% de su optimización debería ocurrir en la etapa de diseño, en función de los requisitos actuales percibidos y, lo que es más importante, los requisitos futuros. Si tiene que sacar un generador de perfiles porque su aplicación no se adapta a la carga requerida, lo dejó demasiado tarde y, en mi opinión, perderá mucho tiempo y esfuerzo sin corregir el problema.

Normalmente, las únicas optimizaciones que valen la pena son aquellas que le otorgan una mejora de rendimiento de un orden de magnitud en términos de velocidad, o un multiplicador en términos de almacenamiento o ancho de banda. Estos tipos de optimizaciones generalmente se relacionan con la selección de algoritmos y la estrategia de almacenamiento, y son extremadamente difíciles de invertir en el código existente. Pueden llegar a influir en la decisión sobre el idioma en el que implementas tu sistema.

Así que mi consejo es que optimice con anticipación, según sus requisitos, no su código, y observe la posible vida útil prolongada de su aplicación.


6
No estoy de acuerdo con su conclusión de "lo dejé demasiado tarde". Básicamente, la creación de perfiles es necesaria cuando una suposición no se cumple, y el generador de perfiles es necesario para decirle QUÉ suposición se rompió. Por ejemplo, encontré que "eliminar el carácter en la posición 0" para StringBuffers en Java funcionó bien para las pruebas junit, pero MUY lento para cadenas grandes. ¡No sospeché ese código hasta que el generador de perfiles lo identificó como el culpable!
Thorbjørn Ravn Andersen

7
Estoy de acuerdo con "cuando necesitas el generador de perfiles, ya es demasiado tarde", según mi experiencia: la mayoría de mis problemas de rendimiento no son cuellos de botella singulares, sino que se extienden entre múltiples contribuyentes. Pero entonces, tengo una sólida experiencia en código y costo de bajo nivel, y habría rechazado instintivamente cualquier cosa que se basara en la eliminación (repetida significativamente) del primer carácter de cadena. +1 para "optimizar durante el diseño".
peterchen

@peterchen solo por curiosidad, ¿qué habrías hecho para "eliminar el primer carácter de cadena"?
Ghos3t

1
@ user258365: La fuerza bruta sería utilizar una representación de cadena que no necesita hacer una copia para las subcadenas. Eso es "casi trivial" para cadenas contadas de referencia inmutables. Alternativamente, cambios algorítmicos, como reemplazar (pseudocódigo) while (s[0]==' ') s = s.substring(1) for(i=0; i<s.len && s[i]==' '; ++i); s=s.substring(i)--- pero esto requiere conocer los problemas potenciales de rendimiento (los perfiladores son herramientas valiosas para el aprendizaje constante aquí).
Peterchen

@ ThorbjørnRavnAndersen, trabajé como consultor para ayudar a un equipo a terminar un proyecto, pero no fue posible porque no se planearon problemas graves de rendimiento (además del código espagueti). Se suponía que debía mostrar un cuadro cronológico con el historial de todos los pacientes. Se realizó una sola solicitud para todos los datos, como Google Maps que busca todo el mundo. Desarrollar un código incorrecto, esperar la elaboración de perfiles más tarde, hizo que el proyecto fallara.
Pedro Amaral Couto

30

Si no ha realizado un perfil, es prematuro.


3
Estoy de acuerdo con la idea detrás de esto, pero también: a menos que la implementación esté completamente limitada por los ciclos de la CPU, obtener una medición que sea reproducible y generalizada es difícil, y cuanto más estable es, menos realista es también.
peterchen

1
El problema que tengo con la respuesta anterior es que implica que no puede optimizar un algoritmo antes de codificarlo. Mi forma de trabajar suele ser diseñar el algoritmo para cumplir con los requisitos funcionales. Observe si es probable que falle los requisitos de rendimiento (por ejemplo, un alto orden de complejidad y es probable que alcance grandes conjuntos de datos) y optimice el algoritmo antes de comenzar a codificarlo. La optimización es simplemente un refinamiento para alcanzar una solución óptima, a menudo se realiza de manera más eficiente en la etapa de diseño.
SmacL

2
No estoy de acuerdo Knuth estaba hablando de pequeñas eficiencias. La optimización a menudo ocurre en la etapa de diseño. Implica la selección de estructuras de datos y algoritmos adecuados, que a menudo tienen un gran impacto en el rendimiento y no necesariamente se pueden intercambiar más adelante.
Haslersn

@haslersn: "Knuth estaba hablando de pequeñas eficiencias" Donald Knuth: "La sabiduría convencional compartida por muchos de los ingenieros de software actuales exige ignorar la eficiencia en lo pequeño; pero creo que esto es simplemente una reacción exagerada a los abusos (...) En disciplinas de ingeniería establecidas una mejora del 12%, de fácil obtención, nunca se considera marginal (...) "
Pedro Amaral Couto

27

Mi pregunta es, si una técnica en particular es diferente pero no particularmente oscura u ofuscada, ¿se puede realmente considerar una optimización prematura?

Um ... Entonces tienes dos técnicas a mano, idénticas en costo (mismo esfuerzo para usar, leer, modificar) y una es más eficiente. No, utilizar el más eficiente no sería, en ese caso, prematuro.

Interrumpir la escritura de su código para buscar alternativas a las construcciones de programación / rutinas de biblioteca comunes en caso de que haya una versión más eficiente en algún lugar, aunque, por lo que sabe, la velocidad relativa de lo que está escribiendo nunca importará. .. Eso es prematuro.


3
De acuerdo, si sabe que un algoritmo es más eficiente para su caso de uso, use el más eficiente. Si no conoce el algoritmo más eficiente, use lo que tiene y perfile más adelante para ver si hay un problema.
grepsedawk

10

Este es el problema que veo con todo el concepto de evitar la optimización prematura.

Hay una desconexión entre decirlo y hacerlo.

He realizado muchos ajustes de rendimiento, exprimiendo grandes factores del código que de otro modo estaría bien diseñado, aparentemente sin una optimización prematura. He aquí un ejemplo.

En casi todos los casos, la razón del rendimiento subóptimo es lo que yo llamo generalidad galopante , que es el uso de clases abstractas de múltiples capas y un diseño orientado a objetos minucioso, donde los conceptos simples serían menos elegantes pero completamente suficientes.

Y en el material didáctico donde se enseñan estos conceptos abstractos de diseño, como la arquitectura basada en notificaciones y el ocultamiento de información, donde simplemente establecer una propiedad booleana de un objeto puede tener un efecto dominó ilimitado de las actividades, ¿cuál es la razón dada? Eficiencia .

Entonces, ¿fue una optimización prematura o no?


Me gusta esta respuesta, ya que ilustra uno de los principales problemas con la abstracción y la generalización. A medida que generaliza una jerarquía de clases para admitir una gama más amplia de casos de uso, es muy fácil afectar seriamente el rendimiento para los casos de uso más típicos. También es fácil adherirse a una clase que proporciona una función determinada sin comprobar si la funcionalidad se proporciona a un nivel aceptable de rendimiento para la escala de uso previsto.
SmacL

1
"donde los conceptos simples serían menos elegantes pero completamente suficientes" El código complejo rara vez es más elegante que el código simple cuando el código simple cumple con los requisitos. (Aunque, yo diría que debe asegurarse de que su código simple realmente explote con una indicación clara del estado / entrada no admitidos si alguien intenta ejecutarlo en un caso más complejo).
jpmc26

8

Primero, haz que el código funcione. En segundo lugar, verifique que el código sea correcto. Tercero, hazlo rápido.

Cualquier cambio de código que se realice antes de la etapa 3 es definitivamente prematuro. No estoy del todo seguro de cómo clasificar las elecciones de diseño hechas antes de eso (como usar estructuras de datos adecuadas), pero prefiero desviarme hacia el uso de abstracciones con las que es fácil programar en lugar de aquellas que tienen un buen rendimiento, hasta que estoy en una etapa en la que puedo comenzar a usar la creación de perfiles y tener una implementación de referencia correcta (aunque con frecuencia lenta) para comparar los resultados.


8

Desde la perspectiva de una base de datos, no considerar el diseño óptimo en la etapa de diseño es, en el mejor de los casos, una temeridad. Las bases de datos no se refactorizan fácilmente. Una vez que están mal diseñados (esto es lo que es un diseño que no considera la optimización, sin importar cómo intente esconderse detrás de la tontería de la optimización prematura), casi nunca se recupera porque la base de datos es demasiado básica para el funcionamiento de todo el sistema. Es mucho menos costoso diseñar correctamente considerando el código óptimo para la situación que esperas que esperar hasta que haya un millón de usuarios y la gente grite porque usaste cursores en toda la aplicación. Otras optimizaciones, como el uso de código sargeable, la selección de los mejores índices posibles, etc., solo tienen sentido en el momento del diseño. Hay una razón por la que se llama así rápido y sucio. Debido a que nunca puede funcionar bien, no use la rapidez como sustituto de un buen código. También, francamente, cuando comprende el ajuste del rendimiento en las bases de datos, puede escribir código que tenga más probabilidades de funcionar bien en el mismo tiempo o menos de lo que se necesita para escribir código que no funciona bien. No tomarse el tiempo para aprender qué es un buen diseño de bases de datos es una pereza del desarrollador, no una mejor práctica.


7

De lo que parece estar hablando es de optimización como el uso de un contenedor de búsqueda basado en hash frente a uno indexado como una matriz cuando se realizarán muchas búsquedas clave. Esta no es una optimización prematura, sino algo que debe decidir en la fase de diseño.

El tipo de optimización de la que se trata la regla de Knuth es minimizar la longitud de las rutas de código más comunes, optimizar el código que se ejecuta más, por ejemplo, reescribiendo en ensamblador o simplificando el código, haciéndolo menos general. Pero hacer esto no sirve de nada hasta que esté seguro de qué partes del código necesitan este tipo de optimización y la optimización hará (¿podría?) Hacer que el código sea más difícil de entender o mantener, por lo tanto, "la optimización prematura es la raíz de todos los males".

Knuth también dice que siempre es mejor, en lugar de optimizar, cambiar los algoritmos que usa su programa, el enfoque que toma para un problema. Por ejemplo, mientras que un pequeño ajuste puede darle un aumento del 10% en la velocidad con la optimización, cambiar fundamentalmente la forma en que funciona su programa podría hacerlo 10 veces más rápido.

En reacción a muchos de los otros comentarios publicados sobre esta pregunta: ¡selección de algoritmo! = Optimización


6

El punto de la máxima es que, por lo general , la optimización es intrincada y compleja. Y , por lo general , usted, el arquitecto / diseñador / programador / mantenedor, necesita un código claro y conciso para comprender lo que está sucediendo.

Si una optimización en particular es clara y concisa, no dude en experimentar con ella (pero retroceda y compruebe si esa optimización es eficaz). El punto es mantener el código claro y conciso durante todo el proceso de desarrollo, hasta que los beneficios del rendimiento superen los costos inducidos de escribir y mantener las optimizaciones.


2
En realidad, un poco de "optimización" se reduce a elegir el algoritmo adecuado para el trabajo; es una actividad de alto nivel con resultados de alto nivel, muy lejos de las "pequeñas eficiencias" en la cita de Knuth.
Shog9

4

Intento optimizar solo cuando se confirma un problema de rendimiento.

Mi definición de optimización prematura es 'esfuerzo desperdiciado en código que no se sabe que sea un problema de rendimiento'. Definitivamente hay un momento y un lugar para la optimización. Sin embargo, el truco consiste en gastar el costo adicional solo cuando sea importante para el rendimiento de la aplicación y cuando el costo adicional supere el impacto en el rendimiento.

Al escribir código (o una consulta de base de datos) me esfuerzo por escribir código 'eficiente' (es decir, código que realiza su función prevista, rápida y completamente con la lógica más simple razonable). Tenga en cuenta que el código 'eficiente' no es necesariamente lo mismo que 'optimizado' código. Las optimizaciones a menudo introducen una complejidad adicional en el código, lo que aumenta tanto el costo de desarrollo como el de mantenimiento de ese código.

Mi consejo: intente pagar el costo de la optimización solo cuando pueda cuantificar el beneficio.


4

Al programar, una serie de parámetros son vitales. Entre estos se encuentran:

  • Legibilidad
  • Mantenibilidad
  • Complejidad
  • Robustez
  • Exactitud
  • Actuación
  • Tiempo de desarrollo

La optimización (buscar rendimiento) a menudo se produce a expensas de otros parámetros y debe equilibrarse con la "pérdida" en estas áreas.

Cuando tiene la opción de elegir algoritmos conocidos que funcionan bien, el costo de "optimizar" por adelantado suele ser aceptable.


1
Le falta el parámetro de control de calidad más importante de la lista anterior; Cumplimiento de requisitos. Si una pieza de software no cumple con los requisitos de la audiencia prevista, todos los demás parámetros carecen de significado. Si el rendimiento no es aceptable, no se han cumplido los requisitos.
SmacL

3
Se podría decir que está cubierto por la corrección. Además, "rendimiento" en el sentido de "lo más rápido posible" rara vez se encuentra entre los requisitos, e incluso el punto de Ola de que sea una compensación con las otras necesidades sigue siendo cierto.
frankodwyer

4

La optimización puede ocurrir en diferentes niveles de granularidad, desde un nivel muy alto hasta un nivel muy bajo:

  1. Comience con una buena arquitectura, acoplamiento flexible, modularidad, etc.

  2. Elija las estructuras de datos y los algoritmos adecuados para el problema.

  3. Optimice la memoria, tratando de ajustar más código / datos en la caché. El subsistema de memoria es de 10 a 100 veces más lento que la CPU, y si sus datos se paginan en el disco, es de 1000 a 10,000 veces más lento. Ser cauteloso con el consumo de memoria es más probable que proporcione mayores beneficios que optimizar las instrucciones individuales.

  4. Dentro de cada función, haga un uso adecuado de las declaraciones de control de flujo. (Mueva las expresiones inmutables fuera del cuerpo del bucle. Ponga el valor más común primero en un interruptor / caso, etc.)

  5. Dentro de cada declaración, use las expresiones más eficientes que produzcan el resultado correcto. (Multiplicar contra cambio, etc.)

La meticulosidad sobre si se debe utilizar una expresión de división o una expresión de cambio no es necesariamente una optimización prematura. Solo es prematuro si lo hace sin optimizar primero la arquitectura, las estructuras de datos, los algoritmos, la huella de memoria y el control de flujo.

Y, por supuesto, cualquier optimización es prematura si no define un umbral de rendimiento objetivo.

En la mayoría de los casos, ya sea:

A) Puede alcanzar el umbral de rendimiento de la meta realizando optimizaciones de alto nivel, por lo que no es necesario jugar con las expresiones.

o

B) Incluso después de realizar todas las optimizaciones posibles, no alcanzará el umbral de rendimiento de su objetivo y las optimizaciones de bajo nivel no suponen una diferencia suficiente en el rendimiento para justificar la pérdida de legibilidad.

En mi experiencia, la mayoría de los problemas de optimización se pueden resolver a nivel de arquitectura / diseño o estructura de datos / algoritmo. La optimización para la huella de memoria es a menudo (aunque no siempre) necesaria. Pero rara vez es necesario optimizar el control de flujo y la lógica de expresión. Y en aquellos casos en los que realmente es necesario, rara vez es suficiente.


3

La respuesta de Norman es excelente. De alguna manera, rutinariamente realiza alguna "optimización prematura" que son, en realidad, las mejores prácticas, porque se sabe que hacer lo contrario es totalmente ineficiente.

Por ejemplo, para agregar a la lista de Norman:

  • Usar la concatenación de StringBuilder en Java (o C #, etc.) en lugar de String + String (en un bucle);
  • Evitar hacer un bucle en C como: for (i = 0; i < strlen(str); i++)(porque strlen aquí es una llamada de función que recorre la cadena cada vez, llamada en cada bucle);
  • Parece que en la mayoría de las implementaciones de JavaScript, también es más rápido de hacer for (i = 0 l = str.length; i < l; i++)y todavía es legible, así que está bien.

Y así. Pero tales microoptimizaciones nunca deberían tener el costo de la legibilidad del código.


3

La necesidad de utilizar un perfilador debe dejarse para casos extremos. Los ingenieros del proyecto deben saber dónde se encuentran los cuellos de botella en el rendimiento.

Creo que la "optimización prematura" es increíblemente subjetiva.

Si estoy escribiendo algún código y que debería usar una tabla hash, lo haré. No lo implementaré de alguna manera defectuosa y luego esperaré a que el informe de error llegue un mes o un año después cuando alguien tenga un problema con él.

El rediseño es más costoso que optimizar un diseño de formas obvias desde el principio.

Obviamente, algunas cosas pequeñas se perderán la primera vez, pero rara vez son decisiones clave de diseño.

Por lo tanto: NO optimizar un diseño es, en mi opinión, un olor a código en sí mismo.


El problema es que a menudo surgen cuellos de botella en secciones de código que nunca pensó que serían un problema. La elaboración de perfiles prescinde de la simulación y muestra los centros de costos reales del programa. Es mejor hacer cosas obvias desde el principio, pero para todo lo demás hay perfiles.
Chris Smith

2

Vale la pena señalar que la cita original de Knuth provino de un artículo que escribió promoviendo el uso gotoen áreas cuidadosamente seleccionadas y medidas como una forma de eliminar puntos críticos. Su cita fue una salvedad que agregó para justificar su razón de ser para usar gotocon el fin de acelerar esos ciclos críticos.

[...] nuevamente, esto es un ahorro notable en la velocidad de funcionamiento general, si, digamos, el valor promedio de n es aproximadamente 20, y si la rutina de búsqueda se realiza aproximadamente un millón de veces en el programa. Tales optimizaciones de bucle [usando gotos] no son difíciles de aprender y, como he dicho, son apropiadas solo en una pequeña parte de un programa, sin embargo, a menudo producen ahorros sustanciales. [...]

Y continúa:

La sabiduría convencional compartida por muchos de los ingenieros de software actuales exige ignorar la eficiencia en lo pequeño; pero creo que esto es simplemente una reacción exagerada a los abusos que ven ser practicados por programadores tontos que no pueden depurar ni mantener 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 de software. Por supuesto que no me molestaría en hacer tales optimizaciones en un solo trabajo, pero cuando se trata de preparar programas de calidad, no quiero restringirme a herramientas que me niegan tales eficiencias [es decir, goto declaraciones en este contexto].

Tenga en cuenta cómo usó "optimizado" entre comillas (el software probablemente no sea realmente eficiente). También tenga en cuenta que no solo está criticando a estos programadores "tontos y tontos", sino también a las personas que reaccionan sugiriendo que siempre se deben ignorar las pequeñas ineficiencias. Finalmente, a la parte que se cita con frecuencia:

No hay duda de que el grial de la eficiencia conduce al abuso. Los programadores pierden una enorme cantidad de tiempo pensando o preocupándose por la velocidad de las partes no críticas de sus programas, y estos intentos de eficiencia en realidad tienen un fuerte impacto negativo cuando se consideran la depuración y el mantenimiento. Deberíamos olvidarnos de las pequeñas eficiencias, digamos el 97% del tiempo; La optimización prematura es la fuente de todos los males.

... y luego algo más sobre la importancia de las herramientas de creación de perfiles:

A menudo es un error hacer juicios a priori sobre qué partes de un programa son realmente críticas, ya que la experiencia universal de los programadores que han estado usando 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 a partir de ahora deberían estar diseñados para proporcionar a todos los programadores comentarios que indiquen qué partes de sus programas están costando más; de hecho, esta información debe proporcionarse automáticamente a menos que se haya desactivado específicamente.

La gente ha hecho un mal uso de su cita en todas partes, a menudo sugiriendo que las microoptimizaciones son prematuras cuando todo su artículo abogaba por microoptimizaciones. Uno de los grupos de personas que estaba criticando que se hacen eco de esta "sabiduría convencional", como él dijo de ignorar siempre las eficiencias en lo pequeño, a menudo hace un mal uso de su cita, que originalmente estaba dirigida, en parte, contra esos tipos que desalientan todas las formas de microoptimización. .

Sin embargo, era una cita a favor de las microoptimizaciones aplicadas de manera apropiada cuando las usa una mano experimentada que sostiene un perfilador. El equivalente analógico actual podría ser como: "La gente no debería intentar optimizar su software, pero los asignadores de memoria personalizados pueden marcar una gran diferencia cuando se aplican en áreas clave para mejorar la localidad de referencia" , o " Código SIMD escrito a mano con un Por lo tanto, una repetición es realmente difícil de mantener y no debería usarla por todas partes, pero puede consumir memoria mucho más rápido cuando se aplica adecuadamente por una mano experimentada y guiada " .

Cada vez que intente promover microoptimizaciones cuidadosamente aplicadas como Knuth promovió anteriormente, es bueno incluir una exención de responsabilidad para disuadir a los novatos de emocionarse demasiado y hacer intentos ciegos de optimización, como reescribir todo su software para usar goto. Eso es en parte lo que estaba haciendo. Su cita fue efectivamente parte de un gran descargo de responsabilidad, al igual que alguien que salta en motocicleta sobre un pozo de fuego en llamas podría agregar un descargo de responsabilidad de que los aficionados no deberían intentar esto en casa mientras simultáneamente critican a aquellos que lo intentan sin el conocimiento y el equipo adecuados y se lastiman. .

Lo que él consideró "optimizaciones prematuras" fueron optimizaciones aplicadas por personas que efectivamente no sabían lo que estaban haciendo: no sabían si la optimización era realmente necesaria, no medían con las herramientas adecuadas, tal vez no entendían la naturaleza de su compilador o arquitectura de computadora, y sobre todo, eran "pennywise-and-pound-tont", lo que significa que pasaron por alto las grandes oportunidades para optimizar (ahorrar millones de dólares) al tratar de reducir centavos, y todo mientras crean código que no pueden depurar y mantener más eficazmente.

Si no encaja en la categoría "pennywise-and-pound-tonish", entonces no está optimizando prematuramente según los estándares de Knuth, incluso si está utilizando un gotopara acelerar un ciclo crítico (algo que es poco probable para ayudar mucho contra los optimizadores actuales, pero si lo hiciera, y en un área realmente crítica, entonces no estaría optimizando prematuramente). Si realmente está aplicando lo que está haciendo en áreas que son realmente necesarias y ellos realmente se benefician de ello, entonces lo está haciendo muy bien a los ojos de Knuth.


1

Para mí, la optimización prematura significa tratar de mejorar la eficiencia de su código antes de tener un sistema en funcionamiento, y antes de haberlo perfilado y saber dónde está el cuello de botella. Incluso después de eso, la legibilidad y la capacidad de mantenimiento deberían estar antes que la optimización en muchos casos.


1

No creo que las mejores prácticas reconocidas sean optimizaciones prematuras. Se trata más de grabar tiempo en los posibles problemas de rendimiento según los escenarios de uso. Un buen ejemplo: si quema una semana tratando de optimizar el reflejo sobre un objeto antes de tener pruebas de que es un cuello de botella, está optimizando prematuramente.


1

A menos que descubra que necesita más rendimiento de su aplicación, ya sea debido a una necesidad del usuario o de la empresa, hay pocas razones para preocuparse por la optimización. Incluso entonces, no hagas nada hasta que hayas perfilado tu código. Luego ataca las partes que requieren más tiempo.


0

A mi modo de ver, si optimizas algo sin saber cuánto rendimiento puedes obtener en diferentes escenarios, ES una optimización prematura. El objetivo del código debería hacer que sea más fácil de leer para los humanos.


0

Como publiqué en una pregunta similar, las reglas de optimización son:

1) No optimices

2) (solo para expertos) Optimizar más tarde

¿Cuándo es prematura la optimización? Generalmente.

La excepción tal vez esté en su diseño o en un código bien encapsulado que se usa mucho. En el pasado, trabajé en algún código de tiempo crítico (una implementación RSA) donde mirar el ensamblador que el compilador produjo y eliminar una sola instrucción innecesaria en un ciclo interno dio un 30% de aceleración. Pero, la aceleración del uso de algoritmos más sofisticados fue órdenes de magnitud más que eso.

Otra pregunta que debe hacerse al optimizar es "¿estoy haciendo aquí el equivalente a optimizar para un módem de 300 baudios?" . En otras palabras, ¿la ley de Moore hará que su optimización sea irrelevante en poco tiempo? Muchos problemas de escalado pueden resolverse simplemente lanzando más hardware al problema.

Por último, pero no menos importante, es prematuro optimizar antes de que el programa vaya demasiado lento. Si está hablando de una aplicación web, puede ejecutarla bajo carga para ver dónde están los cuellos de botella, pero lo más probable es que tenga los mismos problemas de escala que la mayoría de los otros sitios y se aplicarán las mismas soluciones.

editar: Por cierto, con respecto al artículo vinculado, cuestionaría muchas de las suposiciones hechas. En primer lugar, no es cierto que la ley de Moore dejó de funcionar en los años 90. En segundo lugar, no es obvio que el tiempo del usuario sea más valioso que el tiempo del programador. La mayoría de los usuarios (por decir lo menos) no están usando frenéticamente cada ciclo de CPU disponible de todos modos, probablemente están esperando que la red haga algo. Además, existe un costo de oportunidad cuando el tiempo del programador se desvía de la implementación de otra cosa, a reducir unos milisegundos algo que el programa hace mientras el usuario está hablando por teléfono. Cualquier cosa más larga que eso no suele ser optimización, es corrección de errores.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.