En los "buenos tiempos", cuando copiamos shareware en disquetes para amigos, también usamos un poco de ensamblaje. Había una práctica común de "microoptimización", en la que mirabas fijamente y mirabas las líneas de ensamblaje hasta encontrar una manera de expresarlo en una instrucción menos. Incluso hubo un dicho, que era matemáticamente imposible, que " siempre se puede eliminar una instrucción más " . Dado que cambiar el rendimiento en tiempo de ejecución por pequeños factores constantes no es un problema importante para (la mayoría) de la programación actual, los programadores están transfiriendo estos micro- esfuerzos de optimización en otro lugar?
En otras palabras, ¿se puede llevar una mejor práctica a un estado extremo donde ya no agrega nada de valor? ¿Y en cambio está perdiendo el tiempo?
Por ejemplo: ¿Los programadores pierden tiempo generalizando métodos privados que solo se llaman desde un lugar? ¿Se pierde tiempo reduciendo los datos de casos de prueba? ¿Están los programadores (todavía) demasiado preocupados por reducir las líneas de código?
A continuación hay dos excelentes ejemplos de lo que estoy buscando: (1) Pasar tiempo buscando los nombres de variables correctos, incluso cambiando el nombre de todo; y (2) Eliminar incluso la duplicación de código menor y tenue.
Tenga en cuenta que esto es diferente de la pregunta " ¿Para qué optimiza? ", Porque estoy preguntando qué otros programadores parecen maximizar, con el estigma de estas optimizaciones "micro" y, por lo tanto, no un uso productivo del tiempo.
fuente
Respuestas:
Formato de código
fuente
=
y el cuarto para los inicializadores!Solía escribir mucho ensamblador en el día. No es solo que los compiladores hayan mejorado, sino que la mayoría del hardware ahora tiene mucha lógica dedicada a la ejecución de código fuera de orden. El verdadero micro problema es la programación, la mayoría de las instrucciones de la computadora toman varios relojes de máquina para producir un resultado, ¡y una carga de memoria que pierde memoria caché puede tomar varios cientos! Entonces, la idea era programar otras instrucciones para hacer algo útil, en lugar de esperar un resultado. Y las máquinas modernas pueden emitir varias instrucciones por período de reloj. Una vez que comenzamos a ejecutar HW fuera de orden, descubrí que tratar de obtener un gran rendimiento con la codificación manual se convirtió en un juego de tazas. Primero, el HW fuera de orden no ejecutará las instrucciones en su orden cuidadosamente elaborada, La nueva y elegante arquitectura HW ha reducido la penalización de la programación de software inóptima lo suficiente como para que el compilador generalmente esté dentro de un pequeño porcentaje de su rendimiento. También descubrí que los compiladores ahora estaban implementando trucos conocidos pero generadores de complejidad, como desenrollar, cargar el fondo, canalizar el software, etc. La conclusión es que tienes que trabajar muy duro, omite algunos de estos trucos y el compilador te gana. ¡Utilícelos todos y la cantidad de instrucciones de ensamblador que necesita aumentará varias veces!
Probablemente aún más importante, la mayoría de los problemas de rendimiento, no se trata de tasas de problemas de instrucción, sino de introducir los datos en la CPU. Como mencioné anteriormente, la latencia de la memoria ahora es de cientos de ciclos, y la CPU puede ejecutar varias instrucciones por período de reloj, por lo tanto, a menos que el programa, y especialmente las estructuras de datos, estén diseñadas para que la tasa de aciertos de la caché sea extremadamente alta, la microtuning en la instrucción nivel no tendrá resultado. Al igual que los tipos militares dicen que los aficionados hablan de tácticas, los profesionales hablan de logística. La programación de rendimiento ahora es más del 90% de logística (datos móviles). Y esto es difícil de cuantificar, ya que la administración de memoria moderna generalmente tiene múltiples niveles de caché, y las páginas de memoria virtual son manejadas por una unidad de hardware llamada TLB. También la alineación de bajo nivel de las direcciones se vuelve importante, ya que las transferencias de datos reales no están en unidades de bytes, o incluso de 64 bits de largo, pero vienen en unidades de líneas de caché. Entonces, la mayoría de las máquinas modernas tienen hardware que intenta predecir qué línea de caché se pierde en un futuro cercano y emiten prefetches automáticos para llevarlos al caché. Entonces, la realidad es que con las CPU modernas, los modelos de rendimiento son tan complejos que son casi incomprensibles. Incluso los simuladores de hardware detallados nunca pueden coincidir con la lógica exacta de los chips, por lo que el ajuste exacto es simplemente imposible.
Todavía hay un lugar para algunas codificaciones manuales. Las bibliotecas matemáticas (como la función exp), al igual que las operaciones de álgebra lineal más importantes (como la multiplicación de matrices) todavía suelen estar codificadas manualmente por expertos que trabajan para el proveedor de hardware (es decir, Intel o AMD o IBM), pero probablemente solo necesita un par de programadores de ensamblador de primer nivel por cada megacomputadora.
fuente
float
. Por alguna razón que no puedo entender, muchas personas piensan que esto es algo bueno.A veces paso tiempo (¿pierdo?) Mientras elijo un buen nombre para una variable o un método, de modo que no solo sea descriptivo con precisión sino que también tenga un buen estilo lingüístico.
Va un poco más allá cuando intento poner todas las obras (un programa) en el mismo estilo lingüístico. A veces mi opinión cambia y reviso el libro. :)
No, eso lleva tanto tiempo. Es bastante raro. Pero me gusta mantener la buena gramática en mis programas.
fuente
Complejidad del tiempo. Paso demasiado tiempo optimizando las cosas para el peor de los casos en una escala que es mucho mayor que cualquier cosa que sé que el programa encontrará de manera realista.
Soy demasiado obsesivo para dejar ir el hueso 'pero podría crecer ese gran hueso', incluso cuando otras decisiones de diseño impiden que eso suceda de manera realista.
Sin embargo, en mi defensa, si lo poco realista se convierte en realidad ... la optimización realmente ya no es 'micro'. Tenga en cuenta que no dije imposible , pero poco realista .
Por supuesto, los requisitos tienen prioridad.
fuente
Creo que he perdido semanas de tiempo jugando con los controladores de excepciones de Java.
Esto es mucho trabajo para el código que falla dos o tres veces al año. ¿Debería ser esto una advertencia o una información? ¿Error o fatal? ¿Es realmente fatal si el proceso se volverá a generar en cinco minutos? ¿Es realmente una advertencia si todo sigue en estado predeterminado?
Muchas miradas en el ombligo y discusión sobre la naturaleza de un error IO. Si no puede leer un archivo a través de la red, ¿es un error de archivo o un error de red? Y así.
En un momento, reemplacé todas las cadenas de concat
String.format
para que el error de archivo anual no dé como resultado objetos adicionales en el montón. Eso fue un desperdicio.fuente
Supongo que estoy demasiado preocupado por los LOC. No tanto los LOC en sí mismos, sino más bien el número de declaraciones e incluso más número de declaraciones duplicadas. Soy alérgico al código duplicado. En general, me encanta la refactorización, pero supongo que aproximadamente el 50% de lo que hago no hace que el código sea significativamente mejor.
fuente
Leer un archivo línea por línea en lugar de simplemente leer la línea completa en una cadena y procesar la cadena de una sola vez.
Claro, hace una diferencia en la velocidad de ejecución, pero rara vez vale la pena las líneas adicionales de código. Hace que el código sea mucho menos mantenible y aumenta el tamaño del código.
Sí, esto probablemente no tiene sentido si el archivo es de 3 GB pero la mayoría de los archivos no son tan grandes (al menos no con los que estoy trabajando ;-)).
fuente
Cuando estaba escribiendo lenguaje ensamblador, tenía sentido analizar minuciosamente los bytes y los ciclos, pero eso fue hace mucho tiempo y los compiladores han recorrido un largo camino desde entonces. No intentaría optimizar manualmente uno ahora.
Del mismo modo, cuando cambié a escribir en C, fue bastante fácil hacer un mejor trabajo que los compiladores de C, pero cada año Borland, Microsoft o alguien lanzaría uno nuevo y mejorado que pateaba el anterior por la sala. Comencé a prestar atención al código de ensamblaje real que emitía el compilador y me arriesgué si no estaba escribiendo un código agradable y ajustado, desenrollando bucles, moviendo variables fuera de los bucles, etc.
Hoy en día estoy escribiendo en idiomas mucho más altos como Perl, Python y Ruby. Utilizo algunos de los trucos del compilador por adelantado, como desenrollar bucles si tiene sentido, mover variables estáticas fuera de los bucles, etc., pero no me preocupa tanto porque las CPU son un poco más rápidas ahora. Si una aplicación parece arrastrarse inesperadamente, usaré un generador de perfiles y veré qué puedo encontrar, y luego, si se puede mejorar algo, comenzaré a comparar varias formas en que puedo pensar para hacer algo más rápido, y luego veré cómo funciona. escala hacia arriba.
En general trato de ser inteligente en la forma en que escribo el código, basado en años de experiencia.
fuente
Una microoptimización: mejorar el rendimiento de un solo subproceso de cosas que son paralelizables o que simplemente se pueden mejorar con nuevo hardware.
Si algo es lento en una computadora de hace 10 años, una solución más barata es probablemente comprar una computadora más nueva y más rápida, en lugar de perder el tiempo de optimización del programador. Sin embargo, un gran enfoque de las optimizaciones consideradas debería ser encontrar una manera de usar los 8 núcleos que puede obtener hoy, o los 64 que podrá obtener en un par de años, en lugar de agonizar por minucias como el costo adicional de la basura colección.
fuente
La microoptimización en términos de rendimiento en tiempo de ejecución no es un problema cerrado incluso hoy (aunque podría ser menos común). De vez en cuando tengo que explicar a las personas que la sobrecarga de asignar un objeto adicional a cada solicitud o agregar una llamada de función adicional es insignificante.
Aparte de eso, también veo a los programadores micro-optimizados por simplicidad (incluido yo mismo). Todavía tengo que trabajar en algún lugar que no tenga que explicar la diferencia entre simplicidad y simplicidad.
fuente
programmers.
=programmers.stackexchange.com
ser diferente de todos los lugares que usted ha trabajado en el que ha tenido que explicar la diferencia entre estos dos conceptos? Por favor explique la diferencia.Estoy a favor del principio de no optimizar, a menos que sea realmente necesario. Y estoy totalmente de acuerdo con el principio de primer perfil. Y, en principio, solo optimizaré algo que haga la diferencia necesaria.
Eso es en principio. Pero el principio es un mentiroso.
Tengo la costumbre de cuidar las funciones en las bibliotecas de uso frecuente que creo que se usarán mucho en los bucles internos. Y luego, cuando ves algo en otra función, eso no se usa con tanta frecuencia ...
Ah, y luego está la lógica "Lo estoy escribiendo, por supuesto que es una biblioteca importante".
Ah, y por cierto. Nunca he usado un perfilador para guiar una optimización. No tengo acceso a uno comercial, y nunca creé la motivación para descubrir gprof.
Básicamente, soy un hipócrita. Estos principios se aplican a otras personas, pero tengo demasiado miedo de que alguien diga "pero ¿por qué lo escribiste de esa manera lenta y horrible?" así que nunca puedo aplicarlos correctamente a mí mismo.
Sin embargo, no soy tan malo como lo hago aquí. Y soy completamente inmune al autoengaño, ¡así que sabes que tengo razón en eso!
EDITAR
Debo agregar: uno de mis principales estupideces son los gastos generales de llamadas. No en todas partes, por supuesto, pero en esos casos de "creo-que-se-usará-mucho-en-bucles internos" (donde no tengo evidencia objetiva de un problema). He escrito código desagradable basado en offsetof para evitar hacer llamadas a métodos virtuales más de una vez. El resultado puede incluso ser más lento, ya que probablemente no es un estilo de codificación que los optimizadores están diseñados para manejar bien, pero es más inteligente ;-)
fuente
En los viejos tiempos, cuando las computadoras tenían tiempos de reloj medidos en microsegundos, memoria medida en kilobytes y compiladores primitivos, la micro-optimización tenía algún sentido.
La velocidad y el tamaño de las computadoras de la generación actual y la calidad de los compiladores de la generación actual significan que la microoptimización suele ser una pérdida total de tiempo. Las excepciones tienden a ser cuando tiene que obtener el máximo rendimiento de algún código computacionalmente intensivo ... o cuando está escribiendo para un sistema integrado con recursos extremadamente limitados.
Twitter y Facebook vienen a la mente :-)
fuente