¿Cuál es la pesimismo más ridícula que has visto? [cerrado]


145

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? ?


21
"Pesimización" es una gran palabra.
mqp

En caso de que no lo supieras, hablaron sobre tu hilo aquí en el último podcast.
mmcdole

Respuestas:


81

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.


16
Oye, ¿dónde aprendiste tus matemáticas? ¡2 instrucciones con 1 acceso de caché / RAM es obviamente más rápido que 1 instrucción con 1 acceso de caché / RAM!
Razor Storm

2
@RazorStorm En máquinas posteriores donde el ancho de banda y el caché son más valiosos, lo contrario sería cierto. La máscara de bits / cambio es barata, pero desea incluir la mayor cantidad de caché posible y también minimizar el ancho de banda.
Jed

206

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"

  • Lectura de la misma clave de registro miles (o 10 de miles) de veces durante el inicio del programa.
  • Cargando la misma DLL cientos o miles de veces
  • Perdiendo megabytes de memoria al mantener las rutas completas a los archivos innecesariamente
  • No organizan estructuras de datos, por lo que ocupan mucha más memoria de la que necesitan
  • Dimensionar todas las cadenas que almacenan nombres de archivo o rutas a MAX_PATH
  • Encuestas gratuitas para cosas que tienen eventos, devoluciones de llamada u otros mecanismos de notificación.

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í.


46
"Prematuro" es la palabra clave de esa cita. Su reformulación a "optimización sin medir y comprender" no parece cambiar un poco el significado. Eso es precisamente lo que Knuth quiso decir.
Bill the Lizard el

13
@Foredecker: justo en. Demasiada gente olvida el contexto, lo que pone esa cita sólidamente contra micro -Optimización. Analizar un problema para elegir el algoritmo adecuado antes de implementarlo no es prematuro, pero con demasiada frecuencia se presenta esa cita para justificar la solución más floja e ineficiente.
Shog9

55
Realmente depende del caso individual, hay más casos en los que la optimización prematura se convierte en un problema, que la planificación inadecuada de la optimización se convierte en un problema
Mark Rogers

12
-1: Hay una diferencia entre "optimización" y diseño adecuado. Para aquellos que no saben, una buena regla general es que una "optimización" hace que el código sea más difícil de leer, pero más rápido o más eficiente. Un mejor diseño hará que el código sea más fácil de leer (o al menos no peor) y más eficiente.
TED

55
Si se usa en exceso, entonces la población que hace preguntas sobre SO tiene un gran peso hacia los valores atípicos. : D
dkretz

114

Las bases de datos son de pesimismo playland.

Los favoritos incluyen:

  • Divide una tabla en múltiplos (por rango de fechas, rango alfabético, etc.) porque es "demasiado grande".
  • Cree una tabla de archivo para registros retirados, pero continúe UNIENDO con la tabla de producción.
  • Duplicar bases de datos completas por (división / cliente / producto / etc.)
  • Resiste agregar columnas a un índice porque lo hace demasiado grande.
  • Cree muchas tablas de resumen porque el recálculo de datos sin procesar es demasiado lento.
  • Crea columnas con subcampos para ahorrar espacio.
  • Desormalizar en campos como una matriz.

Eso está fuera de mi cabeza.


Resistir la necesidad de indexar es doloroso de pensar.
Bill the Lizard el

2
Sí, conozco a alguien que trabaja para una gran compañía petrolera de EE. UU., Donde casi todas sus tablas tienen una tabla de archivo asociada, y la mayoría de las consultas seleccionan de las vistas que UNIONAN los pares de tablas. ¡El rendimiento es como cabría esperar!
Tony Andrews el

Ja, creo que cada DBA debe haber pasado por la unión con la ruta de la tabla de archivo en algún momento. Siempre parece tan razonable en el momento.
Cruachan

3
Agrego: dividir la base de datos en varias bases de datos diferentes (clientes ac, clientes df, etc.)
Gabriele D'Antona

¿Podría dar más detalles sobre "Desormalizar en campos como una matriz"? ¿A qué te refieres aquí?
Bart van Heukelom

87

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.


37
Estoy de acuerdo "CPU / espacio en disco / memoria es barata" es la verdadera raíz de todo mal. +1
ksuralta

55
También he escuchado esa imitación XML. Otra compañía derrumbada.
n8wrl

19
@ksuralta: "CPU / espacio en disco / memoria es barata" es una excusa conveniente para evitar pensar. Evitar el pensamiento es la raíz imaginaria de todo mal.
Piskvor salió del edificio el

Esta XMLización también ocurrió en mi lugar de trabajo, seguida de JSONization. Todo para evitar un diseño de base de datos relacional "laborioso".
Tanz87

75

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:

  1. Faltan índices en algunas columnas clave.
  2. Unos pocos analistas de datos deshonestos que estaban bloqueando periódicamente las tablas de claves (incluida la "demasiado amplia") consultando la base de datos de producción directamente con MSAccess.

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.


Aaag MSAccess a productos. Escribimos un procedimiento para eliminar todas las conexiones de acceso cada pocos minutos. Finalmente, tp cruzó el mensaje de que era malo.
Nat

1
Teníamos un trabajo similar, pero había quedado en desuso. Para ser justos, el acceso no es el problema, solo facilita a los neófitos la creación / ejecución de consultas sin rendimiento.
JohnFx

En nuestra empresa, dependemos de las conexiones de acceso ad hoc heredadas a la base de datos de producción. ¡Nada como unos pocos SQL'ers casuales para olvidar una cláusula WHERE y bloquear las tablas principales!
HardCode

35
"Escuché que el malva tiene más RAM"
Piskvor salió del edificio el

Podría haber sido peor. Excel Query Editor bloquea la base de datos completa cuando la usa. Cuando no lo sabía, dejé una instancia abierta durante la mayor parte del día mientras trabajaba en otra cosa. La peor parte es que MS SQL Server no informó el nombre de usuario / máquina correcto que estaba haciendo el bloqueo. Horas después me di cuenta de que yo era la razón del bloqueo debido a que las tablas estaban bloqueadas como parte de la vista que estaba consultando y después de haber verificado todo lo demás.
Esteban Küber

58

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!


10
tal vez estaban tratando de incorporar el condensador de flujo
Mikeage

66
Entonces, básicamente, el único principio nuevo involucrado es, en lugar de que se genere energía por el movimiento relativo de los conductores y los flujos, ¿se produce por la interacción modular de la reluctancia magneto y la duración capacitiva?
Matt Rogish el

17
+1 porque mi monitor necesitaba limpieza de todos modos ;-)
RBerteig

1
¡¡Maldición!! Pero, ¿qué pasa con el efecto ecletromagentic-cross-genetical? Creo que también se debe tener en cuenta. O el sujeto puede convertirse en un zombie.
Suraj Chandran

1
Tres palabras: "Rodamientos de silenciador de cromo".
Allbite

53

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

String msg = "Count = " + count + " of " + total + ".";

dentro

StringBuffer sb = new StringBuffer("Count = ");
sb.append(count);
sb.append(" of ");
sb.append(total);
sb.append(".");
String msg = sb.toString();

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:

  1. StringBuilder no está sincronizado, por lo que debería preferirse a StringBuffer en los casos en que su código no se pueda invocar desde varios subprocesos.
  2. Los compiladores Java modernos convertirán la concatenación de cadenas legible en bytecode optimizado para ti cuando sea apropiado de todos modos.

3
Primero: ¿Por qué no usarías al menos Java 5? Segundo: sí puedes. ¿Cómo es que puedes contar hasta 5 en el primer ejemplo, pero no en el segundo? Utiliza los mismos literales de cadena que el primero. Escriba código legible y deje que el compilador decida cuándo usar StringBuffer detrás de escena.
Bill the Lizard el

44
@ MetroidFan2002: Los literales de cadena en el segundo ejemplo también son objetos. Como dije en la respuesta, las diferencias son triviales a esta escala.
Bill the Lizard

1
Eso no significa que reemplace cada String con su propio StringBuffer. La optimización que realiza el compilador reduce el número de objetos creados.
Bill the Lizard

3
@Eric: String msg = "Count =" + count + "de" + total + "."; a menudo se compila en Java para String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... que es precisamente lo que hace el segundo ejemplo.
Grant Wagner

3
Sr. Wagner, la cuestión es que USTED tiene que mirar todas estas llamadas a métodos, no el compilador. Tienes que escribirlos y comprenderlos más tarde. El compilador hace lo mismo de todos modos. Entonces la legibilidad es más importante en este caso.
ypnos

47

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.


42

¿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.


10
Recuerdo que alguien escribió acerca de una aplicación de procesamiento de datos que se vendió en diferentes niveles donde el "Lite" podía dividir solo unos pocos conjuntos de datos por segundo, la versión "superduper" miles. La única diferencia en el código fuente es la suspensión (N).
peterchen

1
¡Brillante! Recomendaría ese estándar en una situación como esa. Al comienzo del desarrollo, asigne una gran cantidad de memoria y agregue algunas llamadas de suspensión, y cada vez que necesite buscar algo de rendimiento, simplemente córtelos. Se llama ser un hacedor de milagros;)
RCIX

Desafortunadamente, parchar Sleeps a NOPs es fácil, por lo que la versión lite se puede descifrar muy fácilmente. Esta reserva de "optimización" podría requerir un empaquetador ejecutable para dificultar la depuración y la aplicación de parches.
TheBlastOne

32

"Independencia de la base de datos". Esto significaba que no había procesos almacenados, disparadores, etc., ni siquiera ninguna clave foránea.


8
¿Es esta "independencia" en el sentido de que estás tan por encima de la base de datos que has olvidado qué datos son? Innecesariamente abstraer sobre bases de datos "para evitar dolores de migración" es un motivo favorito; No lo vas a necesitar.
Rob

8
Más o menos. Arquitectura astronautas en el trabajo. He estado creando aplicaciones web desde que existía, y en todo ese tiempo nunca me he mudado de una plataforma db a otra.
Chris

55
Estoy seguro de que sucede, pero es bastante raro que seas un idiota si diseñas tu arquitectura en torno a esa posibilidad.
Chris

66
Harpo, esa es una situación diferente, es un requisito en ese caso. Estoy hablando de cuando no es un requisito, pero el AA decide que "podría ser" en algún momento.
Chris

3
@Todo: la independencia de DB puede costarle, sí, pero nuestro producto se ejecuta en entornos en los que el proveedor de DB es elegido por las ofertas, y básicamente tenemos que seguir el juego. Algunos desarrolladores no se dan el lujo de una pila de software integrada verticalmente y tienen que arreglárselas a pesar de eso.
Chris R

31
var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();

El mejor uso de un StringBuilder que he visto.


9
Hable acerca de "poco claro sobre el concepto"! ¡Guauu!
Eddie

3
Frio. "Mi líder dice que tengo que usar la clase StringBuilder si quiero concatenar cadenas. Eso es lo que hago. Entonces, ¿qué pasa?" Lol ...
TheBlastOne

26

Usando una expresión regular para dividir una cadena cuando una cadena simple es suficiente.


25
PERO en Java String.Split usa una expresión regular!
Frank Krueger

No veo cómo un Regex podría ser tan rápido como una cadena interna dividida.
Andrei Rînea

2
Pero buscar deliberadamente una expresión regular utilizada para dividir cadenas y reemplazarla con una función de división 'simple' suena como un ejemplo perfecto de pesimismo. Las bibliotecas de expresiones regulares son lo suficientemente rápidas.
David Crawshaw

55
@David Crawshaw: la búsqueda de oportunidades de microoptimización desperdicia el tiempo humano; bud al escribir código, use la solución suficiente menos compleja.
Piskvor salió del edificio el

66
-1: Si estás acostumbrado a expresiones regulares, es muy natural escribir esto en lugar de acostumbrarte a los manipuladores de cadenas internos del lenguaje 1001.
KillianDS

26

Muy tarde para este hilo lo sé, pero lo vi recientemente:

bool isFinished = GetIsFinished();

switch (isFinished)
{
    case true:
        DoFinish();
        break;

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

Ya sabes, por si un booleano tenía algunos valores extra ...


22
Cierto, falso, un archivo no encontrado Por supuesto
Ikke

Oye, siempre debes tener un valor predeterminado / case else / etc. ¿Qué sucede cuando una persona brillante cambia ese valor booleano a una enumeración para reflejar otro estado, luego la siguiente persona agrega la enumeración y se olvida de modificar el procedimiento? Tener un valor predeterminado cuando no es necesario no cuesta tiempo de ejecución y muy poco tiempo de desarrollo. Rastrear un error lógico introducido accidentalmente que ocurre durante el tiempo de ejecución ... Eso cuesta tiempo, dinero y reputación. Una puntada a tiempo ahorra nueve.
Oorang

1
@Oorang ... ¿por qué lo tendrías como un interruptor de todos modos? Es un booleano: un if / else es todo lo que se requiere.
Damovisa

@Damovisa facepalm bien ... muy bien entonces :) Lo perdí :)
Oorang

2
Fue Nullable <Boolean> ... :)
George Chakhidze

25

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.


28
Administración en su máxima expresión: "No, no empleemos 80 horas de programador únicas para arreglar esta aplicación, eso es demasiado costoso. Simplemente conservémoslo, para que sus errores puedan agotar más de 200 horas de usuario por mes, más 10 horas de programador por mes durante 'mantenimiento'." AAAAAAAAAUGH !!!
Piskvor salió del edificio el

25

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.


2
Lo hice yo mismo una vez, cuando determiné que típicamente n = 2. Las mejoras posteriores del producto invalidaron mi premisa, y el código fue reemplazado PDQ.
Mark Ransom

2
Sí, pero es bueno escribir algo basado en algoritmos de vez en cuando;)
UpTheCreek

20

Una vez trabajé en una aplicación que estaba llena de código como este:

 1 tuple *FindTuple( DataSet *set, int target ) {
 2     tuple *found = null;
 3     tuple *curr = GetFirstTupleOfSet(set);
 4     while (curr) {
 5         if (curr->id == target)
 6             found = curr;
 7         curr = GetNextTuple(curr);
 8     }
 9     return found;
10 }

Simplemente quitando found, volviendonull al final y cambiando la sexta línea a:

            return curr;

Duplicó el rendimiento de la aplicación.


1
Trabajé una vez en una compañía donde las pautas de codificación exigían "solo una devolución al final" (para mantener). Y, de hecho, un código de escupir como el suyo, porque no piensan (las soluciones obvias fueron la mayoría de las veces usando un goto a la salida de proceso, o cambiando la condición de salida de los bucles)
flolo

12
Una curva de retorno aquí produce un comportamiento notablemente diferente. Cuando regresas curr, terminas obteniendo la PRIMERA coincidencia, donde a medida que el código que pegaste devuelve la ÚLTIMA coincidencia.
SoapBox

2
@SoapBox: Tienes razón. @Dour High Arch: El aumento en el rendimiento no tuvo nada que ver con la regla de retorno único, ya que flolo dijo que cambiar la condición del bucle a (curr &&! Found) tendría el mismo efecto. GOTO a la salida del proceso es horrible y derrota el propósito de la pauta de retorno único.
Akusete

2
Buenos comentarios a todos. En este caso, se suponía que solo había una tupla con cada ID.
Dour High Arch

77
Sin embargo, eso no es una "pesimización", ¿verdad? Es simplemente una optimización esperando a suceder.
Tim Long

20

Una vez tuve que intentar modificar el código que incluía estas gemas en la clase Constantes

public static String COMMA_DELIMINATOR=",";
public static String COMMA_SPACE_DELIMINATOR=", ";
public static String COLIN_DELIMINATOR=":";

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.


Al menos algo así es fácil de encontrar / reemplazar fuera de la fuente, aún así, mis condolencias.
Erik Forbes

12
También - ¿Deliminador? Pensé que se deletreaba 'delimitador'. Deliminator suena como una mala película de mediados de los 90 que de alguna manera obtuvo 3 sequals ...........
Erik Forbes

53
Deliminator III: Rise of the Comas
Rob

33
En otra nota, me complace ver la delimitación adecuada de Colins. Todo programador que se precie sabe que si hay una cosa que debes separar correctamente, son los malditos Colins.
Rob

2
No es tan fácil hacer una correcta búsqueda y reemplazo. Dado que cada uno se utiliza para diferentes propósitos. Cualquier buen programador habría hecho al menos algo como esto: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... etc
KitsuneYMG el

19

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Á!


9
Sí pasa MySQL -> PostgreSQL, así que no pierda nada.
Thomas

O postgres / postgis -> sqlite / spatialite ... Eso fue un dolor en el culo ...
Philip

sucede en las pruebas JUnit
kachanov

17

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):

int some_method(int input1, int input2) {
    int x;
    if (input1 == -1) {
        return 0;
    }
    if (input1 == input2) {
        return input1;
    }
    ... a long expression here ...
    return x;
}

dentro de esto:

int some_method() {
    return (input == -1) ? 0 : (input1 == input2) ? input 1 :
           ... a long expression ...
           ... a long expression ...
           ... a long expression ...
}

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->structElementera demasiado ilegible, por lo que comenzó a cambiar todo esto en (*ptr).structElementla 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.


Si dicho módulo se llamara millones y millones de veces por ciclo, entonces aprobaría esa optimización siempre y cuando él lo comentara.
Michael Dorgan el

2
@ Michael: No lo haría, a menos que haya mediciones que indiquen que fue más rápido , no solo más corto .
dsimcha

En la mayoría de las situaciones, el operador ternario es más legible que 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 usarlo if).
Leushenko

2
El problema aquí es tomar una función completa y reemplazarla con una sola declaración, un retorno, reemplazando así toda la lógica de la función completa con ternar anidados. Si lo vieras, lo entenderías. Esto no es una cosa religiosa "Odio a los operadores ternarios". No estoy hablando de tomar un single ifen 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.
Eddie

15

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.


Solo evito que esto suceda en un proyecto hoy. Ahora sé que hice la buena elección.
deadalnix

14

Supongo que podría ofrecer esta gema:

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1, root = 0;
    #define ISQRT_INNER(shift) \
    { \
        if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
        { \
            root += 1 << shift; \
            value -= tmp; \
        } \
    }

    // Find out how many bytes our value uses
    // so we don't do any uneeded work.
    if (value & 0xffff0000)
    {
        if ((value & 0xff000000) == 0)
            tmp = 3;
        else
            tmp = 4;
    }
    else if (value & 0x0000ff00)
        tmp = 2;

    switch (tmp)
    {
        case 4:
            ISQRT_INNER(15);
            ISQRT_INNER(14);
            ISQRT_INNER(13);
            ISQRT_INNER(12);
        case 3:
            ISQRT_INNER(11);
            ISQRT_INNER(10);
            ISQRT_INNER( 9);
            ISQRT_INNER( 8);
        case 2:
            ISQRT_INNER( 7);
            ISQRT_INNER( 6);
            ISQRT_INNER( 5);
            ISQRT_INNER( 4);
        case 1:
            ISQRT_INNER( 3);
            ISQRT_INNER( 2);
            ISQRT_INNER( 1);
            ISQRT_INNER( 0);
    }
#undef ISQRT_INNER
    return root;
}

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):

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1, root = 0;
    #define ISQRT_INNER(shift) \
    { \
        if (value >= (tmp = ((root << 1) + (1 << (shift))) << (shift))) \
        { \
            root += 1 << shift; \
            value -= tmp; \
        } \
    }

    ISQRT_INNER (15);
    ISQRT_INNER (14);
    ISQRT_INNER (13);
    ISQRT_INNER (12);
    ISQRT_INNER (11);
    ISQRT_INNER (10);
    ISQRT_INNER ( 9);
    ISQRT_INNER ( 8);
    ISQRT_INNER ( 7);
    ISQRT_INNER ( 6);
    ISQRT_INNER ( 5);
    ISQRT_INNER ( 4);
    ISQRT_INNER ( 3);
    ISQRT_INNER ( 2);
    ISQRT_INNER ( 1);
    ISQRT_INNER ( 0);

#undef ISQRT_INNER
    return root;
}

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:

unsigned long isqrt(unsigned long value)
{
    unsigned long tmp = 1 << 30, root = 0;

    while (tmp != 0)
    {
        if (value >= root + tmp) {
            value -= root + tmp;
            root += tmp << 1;
        }
        root >>= 1;
        tmp >>= 2;
    }

    return root;
}

Este es exactamente el mismo algoritmo, aunque una implementación ligeramente diferente, así que supongo que califica.


Supongo que isqrt()computa floor(sqrt()), pero, ¿por qué funciona este código?
Pablo H

11

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).


No siempre es una mala idea rodar su propio código. Como dijo un sabio una vez, encuentre las dependencias y elimínelas. Si es una función central del negocio, hágalo usted mismo.
Kibbee el

Nunca deduje que siempre es una mala idea. A menos que digas Frans Bouma o similar, dudo que las cosas ORM / DAL sean una función comercial central. Es extremadamente ineficaz escribir su propio equivalente, un caso de reinvención de la rueda (cuadrada), generalmente debido al síndrome NIH.
Gordon Hartley

@Kibbee: estoy de acuerdo. Es mejor rodar el tuyo y comprenderlo que usar dependencias de terceros. Cuando se rompe (y lo hará) al menos, entonces puedes arreglarlo. En el pasado, encontré errores en Hibernate y Apache Commons que estaban matando absolutamente el rendimiento de nuestra aplicación.
CodingWithSpike

44
Uno de ellos es realmente su única opción si ninguno de los establecidos tiene una característica crítica que necesita.
staticsan

3
En realidad, dados algunos de los comentarios anteriores, una perspectiva más: otra pesimismo es tratar de hacer que el ORM haga absolutamente todo. A menudo es útil para el 95% + de los casos. Para ese 5% final, es mucho más fácil abandonar el código de persistencia hecho a mano / llamadas a procedimientos almacenados directos, etc. por rendimiento, simplicidad o ambos.
Gordon Hartley

10

Todas las restricciones de clave externa se eliminaron de una base de datos, porque de lo contrario habría tantos errores.


8

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.


8

Comprobando antes de CADA operación javascript si el objeto sobre el que está operando existe.

if (myObj) { //or its evil cousin, if (myObj != null) {
    label.text = myObj.value; 
    // we know label exists because it has already been 
    // checked in a big if block somewhere at the top
}

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 expectederrores son molestos, pero esta no es la mejor solución para eso.


¿Cuál es la mejor solución entonces? Creo que es descuidado escribir código, donde ocurren errores ocasionalmente, incluso si no tienen consecuencias directas. Por supuesto, no debe hacerlo, si no espera que el objeto sea nulo en ninguna circunstancia, tal vez esto es lo que quiso decir.
Simon

7

¿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.


3
Gracias, estoy harto de estos acrónimos de 'programación extrema' y de cómo la gente los usa para apoyar prácticas perezosas y contraproducentes.
JAL

Los estudios de proyectos reales muestran que el factor real entre los promedios de código único y de código reutilizable es de aproximadamente 3. Entonces 10 es solo el valor "sentido", pero tiene razón por intención.
peterchen

@peterchen: ¿está diciendo que los estudios muestran que lleva tres veces más tiempo escribir un código reutilizable que un código único, o que muestran que lleva tres veces más tiempo convertir un código único en código reutilizable que escribir un código reutilizable? ¿El código reutilizable en primer lugar?
Jeff Sternal

@jeff: IIRC compararon alguna medida de complejidad (lo que sea que pienses de ellos) de fragmentos en línea que se movieron a métodos separados. La complejidad aumenta debido a casos adicionales admitidos, verificación de parámetros, etc. (lo que me hace suponer que los métodos eran bastante pequeños). Permítanme tratar de desenterrar una referencia.
peterchen

6

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.

El Sr. Curran dijo que el equipo de Microsoft Windows había estado analizando cada aspecto del sistema operativo para realizar mejoras. "Pudimos reducir 400 milisegundos del tiempo de apagado al recortar ligeramente la música de apagado del archivo WAV.

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?


Probablemente encontrará que los otros problemas no son tan fáciles de explicar a los no programadores en un sitio web de la BBC.
Tom Leys

Ahora ese es un ángulo que no consideré, tal vez estoy empezando a perder mi cinismo :-)
belugabob

Que 400 ms son 400 ms de consumo de energía. Probablemente insignificante, pero tal vez se acumula con el tiempo. Aún así, no es algo de lo que me preocupe.
ZachS

1
He perdido muchas horas en total esperando que las máquinas virtuales XP se apaguen para poder pasar a lo siguiente. Estoy muy agradecido por un apagado más rápido.
James

1
Curiosamente, los archivos WAV se reproducen de forma asíncrona, por lo que siempre que la fanfarria de apagado sea más corta que el tiempo necesario para apagar, recortar el archivo WAV no hace nada. Y aún más interesante, si optimizaron tanto el apagado, ¿cómo es que cada cuadro de Windows que apago necesita eones hasta que realmente está apagado? (Excepto por usar el botón rojo grande, por supuesto).
TheBlastOne

6

Alguien en mi departamento una vez escribió una clase de cadena. Una interfaz comoCString , 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::stringasignan 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 .


5

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'?"


3
Eso no es una optimización prematura, pensó que necesitaba hacer eso para ser correcto
Pyrolistic

Claro, pensó que lo necesitaba, pero como la mayoría de los DBMS tienen algún tipo de función DAYOFWEEK (marca de tiempo), hacer ese lío por adelantado es bastante prematuro en mi opinión :)
Joril

1
No lo usaría para OLTP, pero si estuviera "analizando los datos del cliente", entonces esa es una forma muy flexible de diseñar un almacén de datos (siempre que la fecha y la hora se dividan en diferentes dimensiones). ¿Realmente desea llamar a DAYOFWEEK () contra millones de filas de datos o simplemente hacer una búsqueda de índice contra un campo entero?
Tim Medora

Bueno, no sé si había tantas filas, pero seguramente esa no es la explicación que se dio :)
Joril

3

Sin ofender a nadie, pero acabo de calificar una tarea (java) que tenía esto

import java.lang.*;

1
A menos que se trate de una clase de nivel superior, creo que debes dejar a esta estudiante un poco floja a menos que le hayas enseñado lo suficiente como para saber por qué no es una buena idea.
Bryan Oakley el

24
¿Seré el único en notar la ironía de un maestro que llama a WTF en el código de un estudiante que él / ella es responsable de enseñar a programar correctamente?
JohnFx

3
Sí, no puedo ver que esto duele. En el peor es surpurfluous. Los estudiantes tienden a recurrir a una consistencia rígida mientras aprenden, e importar java.lang es rígidamente consistente con lo que el alumno aprendió sobre la importación.
cygil

1
Gracias a todos por decirme lo obvio. Era una tarea de biología computacional y no la conté, ni siquiera la mencioné.
Desbordado el

2
@ JohnFX: El calificador y el maestro no siempre son la misma persona.
Eddie
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.