Todos sabemos que la optimización prematura es la raíz de todo mal porque conduce a un código ilegible / imposible de mantener. Peor aún es la pesimización, cuando alguien implementa una "optimización" porque piensa que será más rápido, pero termina siendo más lento, además de ser defectuoso, imposible de mantener, etc. ¿Cuál es el ejemplo más ridículo de esto que has visto? ?
performance
optimization
dsimcha
fuente
fuente
Respuestas:
En un proyecto antiguo heredamos algunos (por lo demás excelentes) programadores de sistemas embebidos que tenían una experiencia masiva con el Z-8000.
Nuestro nuevo entorno era Sparc Solaris de 32 bits.
Uno de los muchachos fue y cambió todas las entradas a cortos para acelerar nuestro código, ya que obtener 16 bits de RAM fue más rápido que obtener 32 bits.
Tuve que escribir un programa de demostración para mostrar que obtener valores de 32 bits en un sistema de 32 bits era más rápido que obtener valores de 16 bits, y explicar que para obtener un valor de 16 bits la CPU tenía que hacer un ancho de 32 bits acceso a la memoria y luego enmascarar o cambiar los bits no necesarios para el valor de 16 bits.
fuente
Creo que la frase "la optimización prematura es la raíz de todo mal" está muy utilizada. Para muchos proyectos, se ha convertido en una excusa para no tener en cuenta el rendimiento hasta el final de un proyecto.
Esta frase es a menudo una muleta para que las personas eviten el trabajo. Veo esta frase utilizada cuando la gente realmente debería decir "Caramba, realmente no pensamos en eso por adelantado y no tenemos tiempo para lidiar con eso ahora".
He visto muchos más ejemplos "ridículos" de problemas de rendimiento tontos que ejemplos de problemas introducidos debido a la "pesimismo"
Lo que creo que es una mejor afirmación es esta: "la optimización sin medir y comprender no es optimización en absoluto, es solo un cambio aleatorio".
El trabajo de buen rendimiento lleva mucho tiempo, a menudo más que el desarrollo de la característica o componente en sí.
fuente
Las bases de datos son de pesimismo playland.
Los favoritos incluyen:
Eso está fuera de mi cabeza.
fuente
Creo que no hay una regla absoluta: algunas cosas se optimizan mejor por adelantado, y otras no.
Por ejemplo, trabajé en una empresa donde recibimos paquetes de datos de satélites. Cada paquete cuesta mucho dinero, por lo que todos los datos están altamente optimizados (es decir, empaquetados). Por ejemplo, la latitud / longitud no se envió como valores absolutos (flotantes), sino como compensaciones en relación con la esquina "noroeste" de una zona "actual". Tuvimos que desempaquetar todos los datos antes de poder usarlos. Pero creo que esto no es pesimismo, es una optimización inteligente para reducir los costos de comunicación.
Por otro lado, nuestros arquitectos de software decidieron que los datos desempaquetados deberían formatearse en un documento XML muy legible y almacenarse en nuestra base de datos como tal (en lugar de tener cada campo almacenado en una columna correspondiente). Su idea era que "XML es el futuro", "el espacio en disco es barato" y "el procesador es barato", por lo que no había necesidad de optimizar nada. ¡El resultado fue que nuestros paquetes de 16 bytes se convirtieron en documentos de 2kB almacenados en una columna, e incluso para consultas simples tuvimos que cargar megabytes de documentos XML en la memoria! Recibimos más de 50 paquetes por segundo, por lo que puede imaginar cuán horrible se volvió el rendimiento (BTW, la compañía se declaró en quiebra).
De nuevo, no hay una regla absoluta. Sí, a veces la optimización demasiado pronto es un error. Pero a veces el lema "cpu / disk space / memory is cheap" es la verdadera raíz de todo mal.
fuente
Oh Dios mío, creo que los he visto a todos. La mayoría de las veces es un esfuerzo para solucionar los problemas de rendimiento por parte de alguien que es demasiado perezoso para resolver el problema de la CAUSA de esos problemas de rendimiento o incluso investigar si realmente hay un problema de rendimiento. En muchos de estos casos, me pregunto si no es solo el caso de esa persona que quiere probar una tecnología en particular y busca desesperadamente un clavo que se ajuste a su nuevo y brillante martillo.
Aquí hay un ejemplo reciente:
El arquitecto de datos viene a mí con una propuesta elaborada para particionar verticalmente una tabla clave en una aplicación bastante grande y compleja. Quiere saber qué tipo de esfuerzo de desarrollo sería necesario para adaptarse al cambio. La conversación fue así:
Yo: ¿Por qué estás considerando esto? ¿Cuál es el problema que estás tratando de resolver?
Él: la tabla X es demasiado amplia, la estamos dividiendo por razones de rendimiento.
Yo: ¿Qué te hace pensar que es demasiado ancho?
Él: El consultor dijo que hay demasiadas columnas para tener en una tabla.
Yo: ¿ Y esto está afectando el rendimiento?
Él: Sí, los usuarios han reportado ralentizaciones intermitentes en el módulo XYZ de la aplicación.
Yo: ¿Cómo sabes que el ancho de la tabla es la fuente del problema?
Él: Esa es la tabla de claves utilizada por el módulo XYZ, y es como 200 columnas. Debe ser el problema.
Yo (Explicando): Pero el módulo XYZ en particular usa la mayoría de las columnas de esa tabla, y las columnas que usa son impredecibles porque el usuario configura la aplicación para mostrar los datos que desea mostrar de esa tabla. Es probable que el 95% del tiempo terminemos uniendo todas las mesas de nuevo, lo que perjudicaría el rendimiento.
Él: El consultor dijo que es demasiado amplio y que debemos cambiarlo.
Yo: ¿Quién es este consultor? No sabía que contratamos a un consultor, ni hablaron con el equipo de desarrollo.
Él: Bueno, todavía no los hemos contratado. Esto es parte de una propuesta que ofrecieron, pero insistieron en que necesitábamos rediseñar esta base de datos.
Yo: Uh huh Entonces, el consultor que vende servicios de rediseño de bases de datos cree que necesitamos un rediseño de la base de datos ...
La conversación siguió y siguió así. Luego, volví a mirar la tabla en cuestión y determiné que probablemente podría reducirse con una simple normalización sin necesidad de estrategias de partición exóticas. Esto, por supuesto, resultó ser un punto discutible una vez que investigué los problemas de rendimiento (previamente no reportados) y los rastreé a dos factores:
Por supuesto, el arquitecto todavía está presionando para una partición vertical de la mesa colgando del metaproblema "demasiado amplio". Incluso reforzó su caso al obtener una propuesta de otro consultor de bases de datos que pudo determinar que necesitábamos cambios importantes en el diseño de la base de datos sin mirar la aplicación o ejecutar ningún análisis de rendimiento.
fuente
He visto personas que usan alphadrive-7 para incubar totalmente CHX-LT. Esta es una práctica poco común. La práctica más común es inicializar el transformador ZT para que se reduzca la amortiguación (debido a una mayor resistencia a la sobrecarga neta) y crear bytegrafías de estilo java.
¡Totalmente pesimista!
fuente
Reconozco que no hay nada que rompa la Tierra, pero he atrapado a personas que usan StringBuffer para concatenar cadenas fuera de un bucle en Java. Era algo simple como girar
dentro
Solía ser una práctica bastante común usar la técnica en un bucle, porque era mucho más rápido. La cuestión es que StringBuffer está sincronizado, por lo que en realidad hay una sobrecarga adicional si solo está concatenando unas pocas cadenas. (Sin mencionar que la diferencia es absolutamente trivial en esta escala). Otros dos puntos sobre esta práctica:
fuente
Una vez vi una base de datos MSSQL que usaba una tabla 'Root'. La tabla raíz tenía cuatro columnas: GUID (identificador único), ID (int), LastModDate (datetime) y CreateDate (datetime). Todas las tablas en la base de datos fueron Clave externa a la tabla raíz. Cada vez que se creaba una nueva fila en cualquier tabla de la base de datos, tenía que usar un par de procedimientos almacenados para insertar una entrada en la tabla raíz antes de poder acceder a la tabla real que le interesaba (en lugar de que la base de datos hiciera el trabajo para usted con unos pocos desencadenantes desencadenantes simples).
Esto creó un desastre de inútiles oídos y dolores de cabeza, requirió todo lo escrito encima para usar sprocs (y eliminó mis esperanzas de presentar LINQ a la compañía. Era posible pero simplemente no valía la pena el dolor de cabeza), y para colmo no lo hizo. Incluso logra lo que se suponía que debía hacer.
El desarrollador que eligió este camino lo defendió asumiendo que esto ahorraba toneladas de espacio porque no estábamos usando Guías en las tablas mismas (pero ... ¿no se genera un GUID en la tabla raíz para cada fila que hacemos?) , mejoró el rendimiento de alguna manera y facilitó la auditoría de los cambios en la base de datos.
Ah, y el diagrama de la base de datos parecía una araña mutante del infierno.
fuente
¿Qué tal POBI - pesimismo obviamente por intención?
Colega mía en los años 90 estaba cansada de ser pateada por el CEO solo porque el CEO pasó el primer día de cada lanzamiento de software ERP (uno personalizado) con la localización de problemas de rendimiento en las nuevas funcionalidades. Incluso si las nuevas funcionalidades crujían gigabytes e hacían posible lo imposible, siempre encontraba algún detalle, o incluso un problema aparentemente importante, para quejarse. Él creía saber mucho acerca de la programación y obtuvo sus patadas pateando traseros de programador.
Debido a la naturaleza incompetente de la crítica (era un CEO, no un tipo de TI), mi colega nunca logró acertar. Si no tiene un problema de rendimiento, no puede eliminarlo ...
Hasta que para un lanzamiento, puso muchas llamadas de función Delay (200) (era Delphi) en el nuevo código. Pasaron solo 20 minutos después de la puesta en marcha, y se le ordenó aparecer en la oficina del CEO para buscar sus insultos vencidos en persona.
Lo único inusual hasta el momento fue que mis colegas se callaron cuando regresó, sonriendo, bromeando, saliendo a tomar un BigMac o dos mientras normalmente pateaba mesas, se quejaba sobre el CEO y la compañía, y pasaba el resto del día muerto. .
Naturalmente, mi colega ahora descansó durante uno o dos días en su escritorio, mejorando sus habilidades de puntería en Quake; luego, en el segundo o tercer día, eliminó las llamadas de Delay, reconstruyó y lanzó un "parche de emergencia" del cual difundió la palabra que había pasado 2 días y 1 noche para arreglar los agujeros de rendimiento.
Esta fue la primera (y única) vez que el malvado CEO dijo "¡buen trabajo!" a él. Eso es todo lo que cuenta, ¿verdad?
Esto fue real POBI.
Pero también es una especie de optimización de procesos sociales, por lo que está 100% bien.
Yo creo que.
fuente
"Independencia de la base de datos". Esto significaba que no había procesos almacenados, disparadores, etc., ni siquiera ninguna clave foránea.
fuente
El mejor uso de un StringBuilder que he visto.
fuente
Usando una expresión regular para dividir una cadena cuando una cadena simple es suficiente.
fuente
Muy tarde para este hilo lo sé, pero lo vi recientemente:
Ya sabes, por si un booleano tenía algunos valores extra ...
fuente
El peor ejemplo que se me ocurre es una base de datos interna de mi empresa que contiene información sobre todos los empleados. Recibe una actualización nocturna de Recursos Humanos y tiene un servicio web ASP.NET en la parte superior. Muchas otras aplicaciones usan el servicio web para llenar cosas como campos de búsqueda / menú desplegable.
El pesimismo es que el desarrollador pensó que las llamadas repetidas al servicio web serían demasiado lentas para realizar consultas SQL repetidas. Entonces, ¿qué hizo él? El evento de inicio de la aplicación se lee en toda la base de datos y lo convierte todo en objetos en la memoria, almacenados indefinidamente hasta que se recicla el grupo de aplicaciones. Este código era tan lento que tomaría 15 minutos cargarlo en menos de 2000 empleados. Si inadvertidamente recicló el grupo de aplicaciones durante el día, podría tomar 30 minutos o más, porque cada solicitud de servicio web iniciaría múltiples recargas simultáneas. Por esta razón, las nuevas contrataciones no aparecerían en la base de datos el primer día cuando se creó su cuenta y, por lo tanto, no podrían acceder a la mayoría de las aplicaciones internas en sus primeros dos días, haciendo girar sus pulgares.
El segundo nivel de pesimismo es que el gerente de desarrollo no quiere tocarlo por miedo a romper las aplicaciones dependientes, pero aun así continuamos teniendo interrupciones esporádicas de aplicaciones críticas en toda la compañía debido al diseño deficiente de un componente tan simple.
fuente
Nadie parece haber mencionado la clasificación, así que lo haré.
Varias veces, descubrí que alguien había hecho a mano una clasificación de burbujas, porque la situación "no requería" una llamada al algoritmo de clasificación rápida "demasiado elegante" que ya existía. El desarrollador quedó satisfecho cuando su selección de burbujas artesanal funcionó lo suficientemente bien en las diez filas de datos que están utilizando para las pruebas. No pasó tan bien después de que el cliente había agregado un par de miles de filas.
fuente
Una vez trabajé en una aplicación que estaba llena de código como este:
Simplemente quitando
found
, volviendonull
al final y cambiando la sexta línea a:Duplicó el rendimiento de la aplicación.
fuente
Una vez tuve que intentar modificar el código que incluía estas gemas en la clase Constantes
Cada uno de estos se utilizó varias veces en el resto de la aplicación para diferentes propósitos. COMMA_DELIMINATOR ensució el código con más de 200 usos en 8 paquetes diferentes.
fuente
El gran número uno de todos los tiempos con el que me encuentro una y otra vez en el software interno:
No usar las características del DBMS por razones de "portabilidad" porque "podríamos querer cambiar a otro proveedor más adelante".
Lee mis labios. Para cualquier trabajo interno: ¡NO PASARÁ!
fuente
Tuve un compañero de trabajo que estaba tratando de burlar al optimizador de nuestro compilador de C y al código de rutina reescrito que solo él podía leer. Uno de sus trucos favoritos era cambiar un método legible como (inventar un código):
dentro de esto:
Es decir, la primera línea de un método que alguna vez fue legible se convertiría en "
return
" y todas las demás lógicas serían reemplazadas por expresiones terciarias profundamente anidadas. Cuando intentaba discutir sobre cómo esto era imposible de mantener, él señalaba el hecho de que el resultado de ensamblaje de su método era tres o cuatro instrucciones de ensamblaje más cortas. No era necesariamente más rápido pero siempre había una pequeña poco más corto. Este era un sistema integrado en el que el uso de memoria ocasionalmente importaba, pero había optimizaciones mucho más fáciles que se podrían haber hecho que esto habría dejado el código legible.Luego, después de esto, por alguna razón decidió que
ptr->structElement
era demasiado ilegible, por lo que comenzó a cambiar todo esto en(*ptr).structElement
la teoría de que también era más legible y más rápido.Convirtiendo el código legible en código ilegible para un máximo del 1% de mejora y, a veces, un código más lento.
fuente
if
. La insistencia en las declaraciones sobre las expresiones en C es un dogma cultural / religioso, no cualquier tipo de práctica objetiva. (Mejor directriz: si el ternario anidado es demasiado largo para leer, tampoco debería usarloif
).if
en una función y reemplazarlo con un ternario. Eso está bien, y a menudo es más legible. Estoy hablando de reemplazar un método completo de más de 30 líneas con una sola declaración de retorno y terrarios anidados. Nadie pensó que el nuevo código fuera más legible, pero un desarrollador pensó que era más rápido.En uno de mis primeros trabajos como desarrollador de pleno derecho, me hice cargo de un proyecto para un programa que estaba sufriendo problemas de escala. Funcionaría razonablemente bien en pequeños conjuntos de datos, pero colapsaría por completo cuando se le den grandes cantidades de datos.
Mientras buscaba, descubrí que el programador original buscaba acelerar las cosas paralelizando el análisis, lanzando un nuevo hilo para cada fuente de datos adicional. Sin embargo, había cometido un error en que todos los hilos requerían un recurso compartido, en el que estaban estancados. Por supuesto, todos los beneficios de la concurrencia desaparecieron. Además, se bloqueó la mayoría de los sistemas al lanzar más de 100 subprocesos solo para bloquear todos menos uno. Mi robusta máquina de desarrollo fue una excepción, ya que se agitó a través de un conjunto de datos de 150 fuentes en alrededor de 6 horas.
Entonces, para solucionarlo, eliminé los componentes de subprocesos múltiples y limpié la E / S. Sin otros cambios, el tiempo de ejecución en el conjunto de datos de 150 fuentes cayó por debajo de los 10 minutos en mi máquina, y desde el infinito hasta menos de media hora en la máquina promedio de la compañía.
fuente
Supongo que podría ofrecer esta gema:
Como la raíz cuadrada se calculó en un lugar muy sensible, tuve la tarea de buscar una forma de hacerlo más rápido. Esta pequeña refactorización redujo el tiempo de ejecución en un tercio (para la combinación de hardware y compilador utilizado, YMMV):
Por supuesto, hay formas más rápidas Y mejores de hacer esto, pero creo que es un buen ejemplo de pesimismo.
Editar: Ahora que lo pienso, el bucle desenrollado también fue en realidad una clara pesimización. Excavando a través del control de versiones, también puedo presentar la segunda etapa de refactorización, que funcionó aún mejor que la anterior:
Este es exactamente el mismo algoritmo, aunque una implementación ligeramente diferente, así que supongo que califica.
fuente
isqrt()
computafloor(sqrt())
, pero, ¿por qué funciona este código?Esto podría estar en un nivel más alto de lo que buscabas, pero arreglarlo (si se te permite) también implica un mayor nivel de dolor:
Insistiendo en rodar una Capa de acceso a datos / Administrador de relación de objetos en lugar de usar una de las bibliotecas establecidas, probadas y maduras (incluso después de que se lo hayan señalado).
fuente
Todas las restricciones de clave externa se eliminaron de una base de datos, porque de lo contrario habría tantos errores.
fuente
Esto no encaja exactamente con la pregunta, pero lo mencionaré de todos modos como una historia de advertencia. Estaba trabajando en una aplicación distribuida que funcionaba lentamente y volé a DC para participar en una reunión destinada principalmente a resolver el problema. El líder del proyecto comenzó a delinear una nueva arquitectura destinada a resolver el retraso. Dije voluntariamente que había tomado algunas medidas durante el fin de semana que aislaron el cuello de botella a un solo método. Resultó que faltaba un registro en una búsqueda local, lo que hace que la aplicación tenga que ir a un servidor remoto en cada transacción. Al volver a agregar el registro a la tienda local, se eliminó el retraso y se resolvió el problema. Tenga en cuenta que la nueva arquitectura no habría solucionado el problema.
fuente
Comprobando antes de CADA operación javascript si el objeto sobre el que está operando existe.
Mi problema con este tipo de código es que a nadie parece importarle ¿qué pasa si no existe? ¿Simplemente no hacer nada? ¿No le das retroalimentación al usuario?
Estoy de acuerdo en que los
Object expected
errores son molestos, pero esta no es la mejor solución para eso.fuente
¿Qué hay del extremismo de YAGNI? Es una forma de pesimismo prematuro. Parece que cada vez que aplicas YAGNI, terminas necesitándolo, lo que resulta en un esfuerzo 10 veces mayor para agregarlo que si lo hubieras agregado al principio. Si crea un programa exitoso, entonces es probable que LO NECESITE. Si está acostumbrado a crear programas cuya vida se agota rápidamente, continúe practicando YAGNI porque supongo que YAGNI.
fuente
No es exactamente una optimización prematura, pero ciertamente está equivocada, esto se leyó en el sitio web de la BBC, en un artículo sobre Windows 7.
Ahora, aún no he probado Windows 7, por lo que podría estar equivocado, pero estoy dispuesto a apostar que hay otros problemas allí que son más importantes que el tiempo que lleva cerrar el sistema. Después de todo, una vez que veo el mensaje "Apagando Windows", el monitor se apaga y me voy, ¿cómo me benefician esos 400 milisegundos?
fuente
Alguien en mi departamento una vez escribió una clase de cadena. Una interfaz como
CString
, pero sin la dependencia de Windows.Una "optimización" que hicieron fue no asignar más memoria de la necesaria. Aparentemente no darse cuenta de que la razón por la cual las clases
std::string
asignan memoria en exceso es para que una secuencia de+=
operaciones pueda ejecutarse en tiempo O (n).En cambio, cada
+=
llamada forzó una reasignación, que convirtió los anexos repetidos en un algoritmo O (n²) Schlemiel the Painter .fuente
Un ex compañero de trabajo mío (un soab , en realidad) fue asignado para construir un nuevo módulo para nuestro ERP de Java que debería haber recopilado y analizado los datos de los clientes (industria minorista). Decidió dividir CADA campo de Calendario / Fecha y hora en sus componentes (segundos, minutos, horas, día, mes, año, día de la semana, bimestre, trimestre (!)) Porque "¿de qué otra manera consultaría 'todos los lunes'?"
fuente
Sin ofender a nadie, pero acabo de calificar una tarea (java) que tenía esto
fuente