Cuando escribo código, siempre trato de hacer mi código lo más limpio y legible posible.
De vez en cuando llega un momento en que necesita cruzar la línea y pasar de un código limpio agradable a un código un poco más feo para hacerlo más rápido.
¿Cuándo está bien cruzar esa línea?
performance
optimization
quality
readability
maintainability
Ken Cochrane
fuente
fuente
Respuestas:
Cruzas la línea cuando
Aquí hay un ejemplo del mundo real: un sistema experimental que estoy ejecutando producía datos demasiado lentamente, tomaba más de 9 horas por ejecución y usaba solo el 40% de la CPU. En lugar de desordenar demasiado el código, moví todos los archivos temporales a un sistema de archivos en memoria. Se agregaron 8 nuevas líneas de código no feo, y ahora la utilización de la CPU está por encima del 98%. Problema resuelto; No se requiere fealdad.
fuente
foo
y renombrarla, por lofoo_ref
general, se encuentra inmediatamente arribafoo
en el archivo fuente. En mi instrumento de prueba que llamofoo
yfoo_ref
para la validación y la medición del rendimiento relativo.Es una falsa dicotomía. Puede hacer que el código sea rápido y fácil de mantener.
La forma de hacerlo es escribirlo limpio, especialmente con una estructura de datos tan simple como sea posible.
Luego descubres dónde están los drenajes de tiempo (ejecutándolo, después de haberlo escrito, no antes), y corríjalos uno por uno. (Aquí hay un ejemplo).
Agregado: Siempre escuchamos sobre compensaciones, ¿verdad, como una compensación entre tiempo y memoria, o una compensación entre velocidad y facilidad de mantenimiento? Si bien tales curvas pueden existir, no se debe suponer que un programa determinado está en la curva , o incluso en cualquier lugar cerca de ella.
Cualquier programa que esté en la curva puede fácilmente (dándole a un cierto tipo de programador) ser mucho más lento y mucho menos sostenible, y entonces no estará cerca de la curva. Dicho programa tiene mucho espacio para ser más rápido y más fácil de mantener.
En mi experiencia, ahí es donde comienzan muchos programas.
fuente
En mi existencia de OSS, realizo una gran cantidad de trabajo en la biblioteca dirigido al rendimiento, que está profundamente vinculado a la estructura de datos de la persona que llama (es decir, externa a la biblioteca), sin (por diseño) ningún mandato sobre los tipos entrantes. Aquí, la mejor manera de hacer este rendimiento es la metaprogramación, que (dado que estoy en .NET-land) significa emisión de IL. Es un código feo, feo, pero muy rápido.
De esta manera, acepto que el código de la biblioteca puede ser "más feo" que el código de la aplicación , simplemente porque tiene menos (o quizás no) control sobre las entradas , por lo que necesita realizar algunas tareas a través de diferentes mecanismos. O como lo expresé el otro día:
Ahora el código de la aplicación es ligeramente diferente, ya que es allí donde los desarrolladores "normales" (sanos) suelen invertir gran parte de su tiempo colaborativo / profesional; Los objetivos y expectativas de cada uno son (IMO) ligeramente diferentes.
En mi opinión, las respuestas anteriores que sugieren que puede ser rápido y fácil de mantener se refieren al código de la aplicación donde el desarrollador tiene más control sobre las estructuras de datos y no utiliza herramientas como la metaprogramación. Dicho esto, hay diferentes formas de hacer metaprogramación, con diferentes niveles de locura y diferentes niveles de sobrecarga. Incluso en ese ámbito, debe elegir el nivel apropiado de abstracción. Pero cuando quiere de manera activa, positiva y genuina que maneje datos inesperados de la manera más rápida; bien puede ponerse feo. Tratar con eso; p
fuente
Cuando ha perfilado el código y verificado que en realidad está causando una desaceleración significativa.
fuente
El código limpio no es necesariamente exclusivo con el código de ejecución rápida. Normalmente, el código difícil de leer se escribió porque era más rápido de escribir, no porque se ejecuta más rápido.
Escribir código "sucio" en un intento de hacerlo más rápido es posiblemente imprudente, ya que no está seguro de que sus cambios realmente mejoren algo. Knuth lo expresó mejor:
En otras palabras, escriba el código limpio primero. Luego, perfile el programa resultante y vea si ese segmento es, de hecho, un cuello de botella de rendimiento. Si es así, optimice la sección según sea necesario y asegúrese de incluir muchos comentarios de documentación (posiblemente incluyendo el código original) para explicar las optimizaciones. Luego perfile el resultado para verificar que realmente haya realizado una mejora.
fuente
Como la pregunta dice " código rápido y difícil de leer ", la respuesta simple es nunca. Nunca hay una excusa para escribir código que sea difícil de leer. ¿Por qué? Dos razones.
fuente
Cuando es código desechable. Lo digo literalmente: cuando escribes un script para realizar un cálculo o tarea única, y sabes con tanta certeza que nunca tendrás que volver a hacer esa acción que puedes 'rm source-file' sin dudarlo, entonces puedes elegir La ruta fea.
De lo contrario, es una dicotomía falsa: si cree que necesita hacerlo feo para hacerlo más rápido, lo está haciendo mal. (O bien, es necesario revisar sus principios sobre qué es un buen código. El uso de goto es de hecho bastante elegante cuando es la solución adecuada al problema. Sin embargo, rara vez lo es).
fuente
Siempre que el costo estimado de menor rendimiento en el mercado sea mayor que el costo estimado de mantenimiento de código para el módulo de código en cuestión.
La gente todavía hace SSE / NEON / etc retorcidos codificados a mano. ensamblaje para tratar de vencer al software de algunos competidores en el popular chip de CPU de este año.
fuente
No olvide que puede hacer que el código difícil de leer sea fácil de entender mediante la documentación y los comentarios adecuados.
En general, el perfil después de haber escrito un código fácil de leer que realiza la función deseada. Los cuellos de botella pueden requerir que haga algo que lo haga parecer más complicado, pero lo arregla explicándose.
fuente
Para mí es una proporción de estabilidad (como en cemento cementado, arcilla cocida al horno, puesta en piedra, escrita con tinta permanente). Cuanto más inestable sea su código, ya que cuanto mayor sea la probabilidad de que tenga que cambiarlo en el futuro, más flexible será, como arcilla húmeda, para mantenerse productivo. También enfatizo la flexibilidad y no la legibilidad. Para mí, la facilidad de cambiar el código es más importante que la facilidad de leerlo. El código puede ser fácil de leer y una pesadilla para cambiar, y ¿de qué sirve poder leer y comprender fácilmente los detalles de implementación si son una pesadilla para cambiar? A menos que sea solo un ejercicio académico, generalmente el punto de poder comprender fácilmente el código en una base de código de producción es con la intención de poder cambiarlo más fácilmente según sea necesario. Si es difícil cambiar, entonces muchos de los beneficios de la legibilidad desaparecen. La legibilidad solo es generalmente útil en el contexto de la flexibilidad, y la flexibilidad solo es útil en el contexto de la inestabilidad.
Naturalmente, incluso el código más difícil de mantener imaginable, independientemente de lo fácil o difícil que sea leerlo, no representa un problema si nunca hay una razón para cambiarlo, solo úselo. Y es posible lograr tal calidad, especialmente para el código de sistema de bajo nivel donde el rendimiento a menudo tiende a contar más. Tengo un código C que todavía uso regularmente, que no ha cambiado desde finales de los 80. No ha necesitado cambiar desde entonces. El código es fugitivo, está escrito en los días poco complicados, y apenas lo entiendo. Sin embargo, todavía es aplicable hoy, y no necesito entender su implementación para aprovecharlo al máximo.
Escribir minuciosamente las pruebas es una forma de mejorar la estabilidad. Otro es el desacoplamiento. Si su código no depende de nada más, entonces la única razón para que cambie es si él mismo necesita cambiar. A veces, una pequeña cantidad de duplicación de código puede servir como un mecanismo de desacoplamiento para mejorar drásticamente la estabilidad de una manera que lo convierte en una compensación digna si, a cambio, obtiene un código que ahora es completamente independiente de cualquier otra cosa. Ahora ese código es invulnerable a los cambios en el mundo exterior. Mientras tanto, el código que depende de 10 bibliotecas externas diferentes tiene 10 veces la razón para que cambie en el futuro.
Otra cosa útil en la práctica es separar su biblioteca de las partes inestables de su base de código, posiblemente incluso compilándola por separado, como lo haría para las bibliotecas de terceros (que también están destinadas a ser utilizadas, no modificadas, al menos no por su equipo). Solo ese tipo de organización puede evitar que las personas lo manipulen.
Otro es el minimalismo. Cuanto menos intente hacer su código, es más probable que pueda hacer lo que hace bien. Los diseños monolíticos son casi permanentemente inestables, ya que cada vez que se les agrega más funcionalidad, más incompletos parecen.
La estabilidad debe ser su objetivo principal siempre que intente escribir código que inevitablemente será difícil de cambiar, como el código SIMD paralelo que ha sido microajustado hasta la muerte. Contrarresta la dificultad de mantener el código maximizando la probabilidad de que no tenga que cambiar el código y, por lo tanto, no tendrá que mantenerlo en el futuro. Eso reduce los costos de mantenimiento a cero, sin importar cuán difícil sea mantener el código.
fuente