language-agnostic optimization

language agnostic - Planificación de la eficiencia temprana versus optimización prematura



language-agnostic optimization (14)

Me parece notar dos escuelas de pensamiento emergentes en cuanto a la optimización:

  1. La optimización prematura es la raíz de todo mal . Solo debe optimizar cuando haya escrito lo más fácil y lo más simple posible. Si después del perfilado determina que el software es demasiado lento, debe optimizarlo.
  2. Las optimizaciones deben hacerse al principio del ciclo de vida de un proyecto . Las optimizaciones deben planificarse, pero deben hacerse razonablemente.

A primera vista, parecen ser puntos de vista bastante opuestos. El caso es que veo el mérito en ambas escuelas de pensamiento. También puedo pensar en momentos en que estas dos formas de pensar me han ayudado a escribir un software mejor y más rápido.

¿Hay alguna manera de reconciliar estas dos ideas? ¿Hay un término medio? ¿Hay un momento en que una idea es la mejor herramienta para el trabajo? ¿O estoy presentando una falsa dicotomía y ambos puntos de vista pueden coexistir pacíficamente?


Adaptando la cita "son tácticas si ganas y es hacer trampa si pierdes", yo diría

Es "planificación de la eficiencia" si funcionó y es una "optimización prematura" si no funciona.


Aquí es donde la planificación entra en juego. Si tiene un gran plan y tiene un buen modelo para lo que está escribiendo, la optimización solo debería tener lugar en la publicación. Si te encuentras que necesitas optimizar mucho tu código, lo más probable es que estés haciendo algo incorrectamente para empezar.

Mucho de esto también vendrá con experiencia y trabajando en tareas similares. En general, la única vez que debe escribir algo que debe optimizarse es cuando está cubriendo cosas con las que nunca antes trabajó. Las optimizaciones suceden en la fase de planificación y en la fase posterior al proyecto IMO.


Concéntrese en escribir código que haga exactamente lo que se supone que debe hacer, y solo la cantidad de veces requerida. La optimización del código limpio y elegante suele ser simple.


Constrúyalo lo mejor posible la primera vez sin agregar mucho tiempo o esfuerzo. Entonces déjalo luchar por tu atención.


El código, además de proporcionar la funcionalidad básica, tiene tres características más que el desarrollador de software debe proporcionar:

  1. Actuación
  2. Mantenibilidad
  3. Robustez

Un código inactivo proporcionaría los tres de estos. con recursos limitados, la llamada de qué porción de código debe optimizarse para lo que debe evaluarse. La optimización de uno de estos, a costa de otros, es peligrosa y debe evitarse en la medida de lo posible.


Hay una verdad básica:

No puedes optimizar lo que no puedes probar

Por lo tanto, como otros han declarado, y especialmente para optimizar el rendimiento, debe escribir el código para luego poder probarlo. Sin embargo, en un gran cuerpo de código, un algoritmo puede ser el más rápido, pero dado que se relaciona con otro código, es una optimización inútil que le cuesta tiempo, o es más lento que la opción 2, 3. ..

Sin embargo, existe un cuerpo de conocimiento que puede aprovechar, especialmente a nivel conceptual, que puede ayudarlo a "preoptimizar" su diseño a escala global.

Desafortunadamente, este es uno de esos debates que no tiene un cierre real.


Las bases de datos en particular no son fáciles de refactorizar y suelen ser el cuello de botella más grande en el sistema debido a los diseñadores que piensan que no deberían preocuparse por el rendimiento cuando diseñan. Esto es miope Hay muchas optimizaciones de bases de datos conocidas que casi siempre serán más rápidas. No utilizarlos en su diseño y codificación inicial para evitar una "optimización prematura" es una tontería. Por ejemplo, un cursor casi nunca (a menos que busque totales en ejecución) rinde mejor que una consulta basada en conjunto en SQl Server. Escribir un cursor en lugar de una consulta basada en conjuntos no es más rápido (una vez que comprende las consultas basadas en conjuntos), por lo que no hay ninguna razón para comenzar con el código basado en el cursor. Lo mismo con las subconsultas de vicevías de tablas derivadas. ¿Por qué escribir el código que usted sabe el 90% del tiempo será más lento que otro código que toma la misma cantidad de tiempo para escribir?

Elegir usar una herramienta que dificulta la configuración del rendimiento también es una decisión miope, por lo que al considerar cómo intenta acceder a la base de datos, esto debería ser parte de lo que considera.

Cualquiera que codifique contra una base de datos o que los diseñe, debería tomarse el tiempo de leer sobre el ajuste del rendimiento para su tipo particular de base de datos. Saber de antemano cómo escribir una consulta detectable y qué tipo de cosas deberían tener los índices con los que empiezas y cuáles son los tipos habituales de cuellos de botella te ayudará a hacer el trabajo mejor la primera vez.


Lo ideal es primero perfilar, luego optimizar cuando sea necesario, pero eso no funciona con el diseño; para cuando tenga algo ejecutable para el perfil, cambiar el diseño sería muy costoso. Por lo tanto, el diseño debe prestar atención a la eficiencia por adelantado. Por lo general, esto significa esbozar algoritmos eficientes por adelantado y mantener la flexibilidad suficiente para cambiar más tarde. (Esto a menudo se realiza mejor con una buena separación de funciones, manteniendo los módulos tan independientes como sea posible, y eso es una buena práctica de diseño por otras razones).

Normalmente, en la (s) fase (s) de diseño, tendrá una buena idea de lo importante que es el rendimiento. Si es necesario, puede diseñar desde el principio el rendimiento (que no incluye optimizaciones a nivel de código).

También está el desarrollo de hábitos de codificación eficientes al elegir entre dos prácticas similares. Por ejemplo, en C ++ vale la pena escribir ++i lugar de i++ , porque es algo trivial que a veces puede ser significativamente más eficiente.

Algo más que eso debería esperar hasta que (a) quede claro que mejorar el rendimiento dará sus frutos, y (b) usted sabe dónde están los puntos de acceso.


Lo que suelo hacer es aplicar esas optimizaciones que no me cuestan nada (o casi nada). También estoy atento a los algoritmos que no se escalan bien y se llaman con mucha frecuencia. Aparte de eso, no optimizo hasta que el software se ejecuta y tengo la oportunidad de iniciar el generador de perfiles. Solo entonces invertiré un tiempo serio en la optimización.


Me gusta la formulación de este tipo .

  1. Optimización mediante el uso de un enfoque general más sensible.
  2. Optimización haciendo que el código sea menos extraño.
  3. Optimización haciendo que el código sea más extraño.

Los primeros dos son ustedes el punto 2. Su 3 es su 1 y el que es la raíz de todo mal. Y tiene razón, las optimizaciones que hacen que el código sea "más extraño" lo hacen más complicado y más difícil de entender, lo que resulta en más errores y dolores de cabeza por mantenimiento.


Optimice a nivel de diseño y arquitectura desde el principio. Micro-optimizar en el nivel de implementación más tarde.

Debe ser consciente de los costos de rendimiento de las decisiones de diseño que tome y que será difícil cambiar más adelante. La implementación a menudo se puede ajustar más adelante, por lo que no vale la pena hacerlo hasta que sepa que es un problema.


Simplemente debe ser un análisis de retorno de la inversión. Si puede poner un poco de esfuerzo en la optimización del diseño y obtener un gran retorno en el rendimiento, hágalo. Gradualmente llegarás al punto en el que el retorno por la cantidad de esfuerzo ya no tiene sentido.


Yo diría que el punto medio sería tener en cuenta las ineficiencias conocidas al escribir el código, pero no optimizarlas anticipadamente si esto significaría un tiempo adicional en el desarrollo inicial o una complejidad adicional.

Mi teoría es: "Escriba código de trabajo simple, luego optimícelo según lo requiera la prueba".


También tendría: Usar estructuras de datos apropiadas y eficientes desde el comienzo . Esto cubre una amplia gama de cosas:

  1. Sepa cómo funcionan todos los contenedores estándar, en qué son buenos y en qué son malos. p.ej. SortedDictionary es rápido en la inserción y búsqueda, pero deficiente en la eliminación. LinkedList es rápido de agregar y eliminar, pero deficiente en la búsqueda, etc.
  2. Sepa dónde estarán sus cuellos de botella. ¿Será CPU, disco, memoria, gráficos, IO, redes, etc. Sepa cómo utilizar cada uno de manera eficiente, cada área requiere diferentes patrones de diseño. Esto realmente depende de la aplicación que se desarrolle también: ¿cuál es la métrica central para concentrarse, para la capacidad de respuesta de la interfaz de usuario, para el buen procesamiento de datos del almacenamiento en caché de disco.
  3. Multhreading. Si la aplicación se escalará a múltiples núcleos, debe decidirse muy temprano en el ciclo de vida de desarrollo si se necesita dicho sistema. El enhebrado en una etapa posterior es mucho más costoso.

Algunas de las respuestas que sabrá por experiencia, algunas requerirán investigación, pero nunca será un trabajo de adivinar.