Qual è la pessimizzazione più ridicola che hai visto? [chiuso]


145

Sappiamo tutti che l'ottimizzazione prematura è la radice di tutti i mali perché porta a codice illeggibile / non realizzabile. Ancora peggio è la pessimizzazione, quando qualcuno implementa una "ottimizzazione" perché pensa che sarà più veloce, ma finisce per essere più lento, oltre ad essere buggy, non mantenibile, ecc. Qual è l'esempio più ridicolo di questo che hai visto ?


21
"Pessimizzazione" è una grande parola.
mqp,

Nel caso in cui non lo sapessi, hanno parlato della tua discussione qui sull'ultimo podcast.
mmcdole,

Risposte:


81

In un vecchio progetto abbiamo ereditato alcuni programmatori di sistemi embedded (altrimenti eccellenti) che avevano una vasta esperienza con Z-8000.

Il nostro nuovo ambiente era Sparc Solaris a 32 bit.

Uno dei ragazzi è andato e ha cambiato tutti gli interi in pantaloncini per accelerare il nostro codice, dal momento che prendere 16 bit dalla RAM era più veloce che afferrare 32 bit.

Ho dovuto scrivere un programma dimostrativo per dimostrare che acquisire valori a 32 bit su un sistema a 32 bit era più veloce che ottenere valori a 16 bit e spiegare che per ottenere un valore a 16 bit la CPU doveva fare una larghezza di 32 bit accesso alla memoria e quindi mascherare o spostare i bit non necessari per il valore a 16 bit.


16
Ehi, dove hai imparato la tua matematica? 2 istruzioni con 1 accesso cache / RAM sono ovviamente più veloci di 1 istruzione con 1 accesso cache / RAM!
Razor Storm,

2
@RazorStorm Su macchine successive in cui larghezza di banda e cache sono più preziose, sarebbe vero il contrario. La maschera di bit / shift è economica, ma si desidera inserire il più possibile nella cache e ridurre al minimo la larghezza di banda.
Jed

206

Penso che la frase "l'ottimizzazione prematura è la radice di tutti i mali" è molto, molto usata. Per molti progetti, è diventata una scusa per non tenere conto delle prestazioni fino a tardi in un progetto.

Questa frase è spesso una stampella per le persone per evitare il lavoro. Vedo questa frase usata quando le persone dovrebbero davvero dire "Accidenti, non ci abbiamo pensato prima e non abbiamo tempo di affrontarlo ora".

Ho visto molti più "ridicoli" esempi di problemi di prestazioni stupide rispetto agli esempi di problemi introdotti a causa della "pessimizzazione"

  • Leggere la stessa chiave di registro migliaia (o decine di migliaia) di volte durante l'avvio del programma.
  • Caricamento della stessa DLL centinaia o migliaia di volte
  • Sprecare mega byte di memoria mantenendo inutili percorsi completi di file
  • Non organizzare le strutture dei dati in modo che occupino molta più memoria del necessario
  • Dimensionamento di tutte le stringhe che memorizzano nomi di file o percorsi su MAX_PATH
  • Polling gratuito per cose che hanno eventi, callback o altri meccanismi di notifica

Quello che penso sia un'affermazione migliore è questa: "l'ottimizzazione senza misurazione e comprensione non è affatto ottimizzazione, è solo un cambiamento casuale".

Il lavoro con buone prestazioni richiede tempo, spesso più che lo sviluppo della funzione o del componente stesso.


46
"Prematuro" è la parola chiave di quella citazione. La tua riformulazione in "ottimizzazione senza misurazione e comprensione" non sembra cambiare il significato un po '. Questo è esattamente ciò che intendeva Knuth.
Bill the Lizard,

13
@Foredecker: giusto. Troppe persone dimenticano contesto, che mette quella citazione solidamente contro micro -ottimizzazione. Analizzare un problema per scegliere l'algoritmo corretto prima di implementarlo non è prematuro, ma troppo spesso tale citazione viene sollevata per giustificare la soluzione più pigra e inefficiente.
Shog9,

5
Dipende molto dal singolo caso, ci sono più casi in cui l'ottimizzazione prematura diventa un problema, che una pianificazione inadeguata dell'ottimizzazione diventa un problema
Mark Rogers,

12
-1: c'è una differenza tra "ottimizzazione" e design adeguato. Per coloro che non sanno dirlo, una buona regola empirica è che una "ottimizzazione" rende il codice più difficile da leggere, ma più veloce o più efficiente. Un design migliore renderà il codice più facile da leggere (o almeno non peggio) e più efficiente.
TED

5
Se è troppo abusato, la popolazione che pone domande su SO è pesantemente ponderata verso gli outlier. : D
dkretz,

114

I database sono il terreno di gioco della pessimizzazione.

I preferiti includono:

  • Dividi una tabella in multipli (per intervallo di date, intervallo alfabetico, ecc.) Perché è "troppo grande".
  • Creare una tabella di archivio per i record ritirati, ma continuare a UNION con la tabella di produzione.
  • Duplica interi database per (divisione / cliente / prodotto / ecc.)
  • Resistere ad aggiungere colonne a un indice perché lo rende troppo grande.
  • Crea molte tabelle di riepilogo perché il ricalcolo dai dati non elaborati è troppo lento.
  • Crea colonne con sottocampi per risparmiare spazio.
  • Denormalizza in campi come un array.

È fuori dalla mia testa.


Resistere alla necessità di indicizzare è solo penoso a cui pensare.
Bill the Lizard,

2
Sì, conosco qualcuno che lavora per una grande compagnia petrolifera americana in cui quasi tutte le sue tabelle hanno una tabella di archivio associata e la maggior parte delle query seleziona da viste che UNION le coppie di tabelle. Le prestazioni sono come ti aspetteresti!
Tony Andrews,

Ah, penso che ogni DBA debba aver abbandonato l'unione con la tabella degli archivi a un certo punto. Sembra sempre così ragionevole al momento.
Cruachan,

3
Aggiungo: dividere il database in diversi database (clienti ac, clienti df, ecc.)
Gabriele D'Antona,

Potresti approfondire "Denormalizzare in campi come un array"? Cosa intendi qui?
Bart van Heukelom,

87

Penso che non esista una regola assoluta: alcune cose sono ottimizzate al meglio in anticipo, altre no.

Ad esempio, ho lavorato in un'azienda in cui abbiamo ricevuto pacchetti di dati dai satelliti. Ogni pacchetto costava un sacco di soldi, quindi tutti i dati erano altamente ottimizzati (cioè imballati). Ad esempio, latitudine / longitudine non sono state inviate come valori assoluti (float), ma come offset relativi all'angolo "nord-ovest" di una zona "corrente". Abbiamo dovuto decomprimere tutti i dati prima che potessero essere utilizzati. Ma penso che questa non sia pessimizzazione, è un'ottimizzazione intelligente per ridurre i costi di comunicazione.

D'altra parte, i nostri architetti del software hanno deciso che i dati decompressi dovrebbero essere formattati in un documento XML molto leggibile e archiviati nel nostro database come tali (invece di avere ogni campo memorizzato in una colonna corrispondente). La loro idea era che "XML è il futuro", "lo spazio su disco è economico" e "il processore è economico", quindi non è stato necessario ottimizzare nulla. Il risultato è stato che i nostri pacchetti da 16 byte sono stati trasformati in documenti da 2kB archiviati in una colonna e per query anche semplici abbiamo dovuto caricare in memoria megabyte di documenti XML! Abbiamo ricevuto oltre 50 pacchetti al secondo, quindi puoi immaginare quanto sia diventata orribile la performance (BTW, la società è fallita).

Quindi, di nuovo, non esiste una regola assoluta. Sì, a volte l'ottimizzazione troppo presto è un errore. Ma a volte il motto "CPU / spazio su disco / memoria è economico" è la vera radice di tutti i mali.


37
Sono d'accordo che "CPU / spazio su disco / memoria è economico" è la vera radice di tutti i mali. +1
ksuralta,

5
Ho sentito anche quel drivel XML. Un'altra compagnia di carri armati.
n8wrl,

19
@ksuralta: "Cpu / spazio su disco / memoria è economico" è una comoda scusa per evitare pensieri. Evitare il pensiero è la radice immaginaria di ogni male.
Piskvor lasciò l'edificio il

Questa XMLizzazione è avvenuta anche sul mio posto di lavoro, seguita da JSONization. Tutto per evitare un progetto di database relazionale "laborioso".
Tanz87,

75

Oh buon Dio, penso di averli visti tutti. Il più delle volte è uno sforzo per risolvere i problemi di prestazioni di qualcuno che è troppo dannatamente pigro per risolvere il problema fino alla CAUSA di quei problemi di prestazioni o persino ricercare se esiste effettivamente un problema di prestazioni. In molti di questi casi mi chiedo se non è solo il caso di quella persona che vuole provare una particolare tecnologia e cerca disperatamente un chiodo che si adatti al suo nuovo martello lucido.

Ecco un esempio recente:

Data Architect mi viene in mente con una proposta elaborata di partizionare verticalmente una tabella di chiavi in ​​un'applicazione abbastanza grande e complessa. Vuole sapere quale tipo di sforzo di sviluppo sarebbe necessario per adeguarsi al cambiamento. La conversazione è andata così:

Io: perché lo stai prendendo in considerazione? Qual è il problema che stai cercando di risolvere?

Lui: la tabella X è troppo ampia, la stiamo partizionando per motivi di prestazioni.

Io: cosa ti fa pensare che sia troppo largo?

Lui: Il consulente ha detto che ci sono troppe colonne per avere in una tabella.

Io: E questo sta influenzando le prestazioni?

Lui: Sì, gli utenti hanno segnalato rallentamenti intermittenti nel modulo XYZ dell'applicazione.

Io: come fai a sapere che la larghezza della tabella è la fonte del problema?

Lui: Questa è la tabella delle chiavi utilizzata dal modulo XYZ, ed è come 200 colonne. Deve essere il problema.

Io (Spiegazione): Ma il modulo XYZ in particolare usa la maggior parte delle colonne in quella tabella e le colonne che utilizza sono imprevedibili perché l'utente configura l'app per mostrare i dati che vogliono visualizzare da quella tabella. È probabile che il 95% delle volte finiremmo per unire tutti i tavoli insieme, il che danneggerebbe le prestazioni.

Lui: Il consulente ha detto che è troppo ampio e dobbiamo cambiarlo.

Io: chi è questo consulente? Non sapevo che avessimo assunto un consulente, né avevano parlato con il team di sviluppo.

Lui: Beh, non li abbiamo ancora assunti. Questo fa parte di una proposta che hanno offerto, ma hanno insistito sulla necessità di riprogettare questo database.

Io: Uh eh. Quindi il consulente che vende servizi di riprogettazione del database pensa che abbiamo bisogno di una riprogettazione del database ....

La conversazione è andata avanti all'infinito. Successivamente, ho dato un'altra occhiata al tavolo in questione e ho determinato che probabilmente poteva essere ristretto con una semplice normalizzazione senza la necessità di strategie di partizionamento esotiche. Questo, ovviamente, si è rivelato essere un punto controverso una volta che ho esaminato i problemi di prestazioni (precedentemente non segnalati) e li ho rintracciati fino a due fattori:

  1. Indici mancanti su alcune colonne chiave.
  2. Alcuni analisti di dati non autorizzati che bloccavano periodicamente le tabelle delle chiavi (inclusa quella "troppo ampia") interrogando il database di produzione direttamente con MSAccess.

Ovviamente l'architetto sta ancora spingendo per un partizionamento verticale del tavolo aggrappato al meta-problema "troppo ampio". Ha anche rafforzato il suo caso ricevendo una proposta da un altro consulente di database che è stato in grado di determinare che avevamo bisogno di importanti modifiche di progettazione al database senza guardare l'app o eseguire alcuna analisi delle prestazioni.


Aaag MSAccesso al prod. Abbiamo scritto una procedura per eliminare tutte le connessioni di accesso ogni pochi minuti e finalmente ho ricevuto il messaggio che era male.
Nat

1
Avevamo un lavoro simile, ma era andato in disuso. Ad essere onesti, Access non è il problema, ma semplifica la creazione / esecuzione di query non performanti per i neofiti.
JohnFx

Nella nostra azienda dipendiamo da connessioni di accesso ad hoc legacy al DB di produzione. Niente come qualche casuale SQL'ers per dimenticare una clausola WHERE e bloccare le tabelle principali!
HardCode,

35
"Ho sentito che il malva ha più RAM"
Piskvor ha lasciato l'edificio il

Sarebbe potuta andare peggio. L'editor di query Excel blocca l'intero database quando lo si utilizza. Quando non lo sapevo, ne ho lasciato aperta un'istanza per gran parte della giornata mentre lavoravo in qualcos'altro. La parte peggiore è che MS SQL Server non ha segnalato il nome utente / la macchina corretti che stava eseguendo il blocco. Ore dopo mi resi conto che ero il motivo del blocco a causa del fatto che le tabelle bloccate facevano parte della vista che stavo interrogando e che avevo controllato prima tutto il resto.
Esteban Küber,

58

Ho visto persone che usano alphadrive-7 per incubare totalmente CHX-LT. Questa è una pratica non comune. La pratica più comune è inizializzare il trasformatore ZT in modo da ridurre la bufferizzazione (a causa della maggiore resistenza al sovraccarico netto) e creare bytegrafazioni in stile java.

Totalmente pessimista!


10
forse stavano cercando di embiggen il condensatore di flusso
Mikeage,

6
Quindi, fondamentalmente, l'unico nuovo principio coinvolto è invece che l'energia venga generata dal moto relativo di conduttori e flussi, prodotta dall'interazione modale di riluttanza magnetica e durabilità capacitiva?
Matt Rogish,

17
+1 perché il mio monitor doveva comunque essere pulito ;-)
RBerteig

1
Dannazione!! Ma che dire dell'effetto ecletromagentic-cross-genetic. Penso che dovrebbe essere preso in considerazione anche. Oppure il soggetto può trasformarsi in uno zombi.
Suraj Chandran,

1
Tre parole: "Chrome Muffler Bearings".
Allbite,

53

Niente di sconvolgente, lo ammetto, ma ho catturato persone che usano StringBuffer per concatenare le stringhe al di fuori di un loop in Java. Era qualcosa di semplice come girare

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

in

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

Una volta era una pratica abbastanza comune usare la tecnica in un ciclo, perché era misurabilmente più veloce. Il fatto è che StringBuffer è sincronizzato, quindi in realtà c'è un sovraccarico extra se stai concatenando solo alcune stringhe. (Per non parlare del fatto che la differenza è assolutamente banale su questa scala.) Altri due punti su questa pratica:

  1. StringBuilder non è sincronizzato, quindi dovrebbe essere preferito su StringBuffer nei casi in cui il codice non può essere chiamato da più thread.
  2. I moderni compilatori Java trasformeranno la concatenazione di stringhe leggibile in bytecode ottimizzato per te quando è appropriato comunque.

3
Primo: perché non dovresti usare almeno Java 5? Secondo: Sì, puoi. Come puoi contare fino a 5 nel primo esempio, ma non nel secondo? Utilizza gli stessi letterali String del primo. Scrivi codice leggibile e lascia che il compilatore decida quando usare StringBuffer dietro le quinte.
Bill the Lizard,

4
@ MetroidFan2002: Anche i letterali String nel secondo esempio sono oggetti. Come ho detto nella risposta, le differenze sono insignificanti su questa scala.
Bill the Lizard,

1
Ciò non significa che sostituisca ogni stringa con il proprio StringBuffer. L'ottimizzazione eseguita dal compilatore riduce il numero di oggetti creati.
Bill the Lizard,

3
@Eric: String msg = "Count =" + count + "di" + total + "."; viene spesso compilato in Java su String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... che è esattamente ciò che fa il secondo esempio.
Grant Wagner,

3
Signor Wagner, il fatto è che devi guardare tutte queste chiamate di metodo, non il compilatore. Devi scriverli e comprenderli in seguito. Il compilatore fa lo stesso comunque. Quindi la leggibilità è più importante in questo caso.
ypnos,

47

Una volta ho visto un database MSSQL che utilizzava una tabella 'Root'. La tabella principale aveva quattro colonne: GUID (uniqueidentifier), ID (int), LastModDate (datetime) e CreateDate (datetime). Tutte le tabelle nel database erano con chiave esterna per la tabella principale. Ogni volta che una nuova riga è stata creata in qualsiasi tabella nel db, dovevi usare un paio di procedure memorizzate per inserire una voce nella tabella principale prima di poter arrivare alla tabella effettiva a cui tieni (piuttosto che al database che fa il lavoro per tu con pochi grilletti grilletti semplici).

Questo ha creato un casino di inutili sentite e mal di testa, ha richiesto qualsiasi cosa scritta sopra per usare sprocs (ed eliminare le mie speranze di presentare LINQ all'azienda. Era possibile ma non valeva la pena il mal di testa), e per finire non ha fatto Non riesco nemmeno a fare quello che doveva fare.

Lo sviluppatore che ha scelto questo percorso lo ha difeso supponendo che questo abbia risparmiato tonnellate di spazio perché non stavamo usando le Guide sui tavoli stessi (ma ... non è un GUID generato nella tabella Root per ogni riga che facciamo?) , in qualche modo ha migliorato le prestazioni e ha reso "facile" il controllo delle modifiche al database.

Oh, e il diagramma del database sembrava un ragno mutante dall'inferno.


42

Che ne dici di POBI - la pessimizzazione ovviamente per intento?

Il mio collega negli anni '90 era stanco di essere preso a calci nel culo dal CEO solo perché il CEO ha trascorso il primo giorno di ogni versione del software ERP (personalizzato) per individuare i problemi di prestazioni nelle nuove funzionalità. Anche se le nuove funzionalità scricchiolavano gigabyte e rendevano possibile l'impossibile, trovava sempre qualche dettaglio, o persino un problema apparentemente importante, su cui lamentarsi. Credeva di sapere molto sulla programmazione e ha preso a calci i calci nel culo del programmatore.

A causa della natura incompetente delle critiche (era un CEO, non un ragazzo IT), il mio collega non è mai riuscito a farlo bene. Se non si riscontra un problema di prestazioni, non è possibile eliminarlo ...

Fino a una versione, ha inserito molte nuove chiamate di funzione Delay (200) (era Delphi) nel nuovo codice. Ci vollero solo 20 minuti dopo il via libera, e gli fu ordinato di presentarsi nell'ufficio del CEO per recuperare di persona gli insulti in ritardo.

L'unica cosa insolita finora era che i miei colleghi erano muti quando tornava, sorrideva, scherzava, usciva per un paio di BigMac mentre normalmente prendeva a calci i tavoli, si incendiava con il CEO e la compagnia e passava il resto della giornata a morire .

Naturalmente, il mio collega ora si riposava per uno o due giorni alla sua scrivania, migliorando le sue abilità di mira in Quake - poi il secondo o il terzo giorno cancellò le chiamate di ritardo, ricostruì e rilasciò una "patch di emergenza" di cui spargeva la voce che aveva trascorso 2 giorni e 1 notte per riparare i buchi delle prestazioni.

Questa è stata la prima (e unica) volta in cui l'amministratore delegato malvagio ha detto "ottimo lavoro!" a lui. Questo è tutto ciò che conta, giusto?

Questa era la vera POBI.

Ma è anche una sorta di ottimizzazione dei processi sociali, quindi va bene al 100%.

Penso.


10
Ricordo che qualcuno scriveva di un'app di elaborazione dati venduta a diversi livelli in cui il "Lite" poteva accartocciare solo alcuni set di dati al secondo, la versione "superduper" migliaia. L'unica differenza del codice sorgente è Sleep (N).
Peter

1
Brillante! consiglierei questo standard in una situazione del genere. All'inizio dello sviluppo allocare una grande porzione di memoria e aggiungere alcune chiamate di sonno, e ogni volta che è necessario ottenere alcune prestazioni, basta ridurle. Si chiama essere un miracoloso;)
RCIX,

Sfortunatamente, patchare Sleep a NOPs è facile, quindi la versione lite può essere decifrata molto facilmente. Questa riserva di "ottimizzazione" potrebbe richiedere un packer eseguibile per rendere più difficile il debug e le patch.
TheBlastOne,

32

"Indipendenza dal database". Ciò significava che non c'erano processi, trigger, ecc. Memorizzati, nemmeno chiavi esterne.


8
Questa "indipendenza" è nel senso che sei molto al di sopra del database, hai dimenticato quali sono i dati? Estrarre inutilmente le basi di dati "per evitare i problemi di migrazione" è una peeve da compagnia; non ne avrai bisogno.
Rob,

8
Abbastanza. Gli astronauti dell'architettura al lavoro. Costruisco app Web da quando esisteva un Web e in tutto quel tempo non mi sono mai spostato da una piattaforma db all'altra.
chris,

5
Sono sicuro che succede, ma è abbastanza raro che tu sia un idiota se progetti la tua architettura attorno a quella possibilità.
chris,

6
Harpo, questa è una situazione diversa - è un requisito in quel caso. Sto parlando di quando non è un requisito, ma l'AA decide che "potrebbe essere" ad un certo punto.
chris,

3
@Tutti: l'indipendenza dei DB può costarti, sì, ma il nostro prodotto viene eseguito in ambienti in cui il fornitore DB viene scelto dalle offerte e in pratica dobbiamo giocare. Alcuni sviluppatori non ottengono il lusso di uno stack software integrato verticalmente e devono accontentarsi di ciò.
Chris R,

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

Il miglior uso di StringBuilder che abbia mai visto.


9
Parla di "poco chiaro sul concetto"! Wow!
Eddie,

3
Freddo. "Il mio lead dice che devo usare la classe StringBuilder se voglio concatenare le stringhe. Ecco cosa faccio. Quindi cosa c'è che non va?" Lol ...
TheBlastOne

26

Usare una regex per dividere una stringa quando è sufficiente un semplice string.split


25
MA in Java String.Split usa una regex!
Frank Krueger,

Non vedo come un Regex potrebbe essere veloce come una divisione di stringhe interna.
Andrei Rînea,

2
Ma cercare deliberatamente un regex utilizzato per la divisione delle stringhe e sostituirlo con una funzione di divisione "semplice" suona come un perfetto esempio di pessimizzazione. Le librerie Regex sono abbastanza veloci.
David Crawshaw,

5
@David Crawshaw: la ricerca di opportunità di microottimizzazione fa perdere tempo all'uomo; gemma durante la scrittura del codice, utilizzare la soluzione sufficiente meno complessa.
Piskvor lasciò l'edificio il

6
-1: se sei abituato alle regex, è molto naturale scrivere questo invece di abituarti ai manipolatori di stringa interni al linguaggio 1001.
KillianDS,

26

Conosco molto tardi questa discussione, ma l'ho visto di recente:

bool isFinished = GetIsFinished();

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

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

Sai, nel caso in cui un booleano avesse dei valori extra ...


22
True, False an FileNotFound ofcourse
Ikke

Ehi, dovresti sempre avere un valore predefinito / case else / etc. Cosa succede quando una persona brillante cambia quel booleano in un enum per riflettere così altro stato, quindi la persona successiva si aggiunge all'enum e si dimentica di modificare la procedura? Avere un valore predefinito quando non è necessario non costi di esecuzione e tempi di sviluppo molto ridotti. Rintracciare un errore logico introdotto accidentalmente che si verifica durante il runtime ... Che costa tempo, denaro e reputazione. Un punto in tempo ne fa risparmiare nove.
Oorang,

1
@Oorang ... perché dovresti averlo comunque come interruttore? È un valore booleano: un if / else è tutto ciò che serve.
Damovisa,

@Damovisa facepalm giusto ... molto bene allora :) Mi mancava :)
Oorang

2
Era Nullable <Boolean> ... :)
George Chakhidze il

25

Il peggior esempio che mi viene in mente è un database interno della mia azienda contenente informazioni su tutti i dipendenti. Riceve un aggiornamento notturno da HR e ha un servizio web ASP.NET in cima. Molte altre app usano il servizio web per popolare cose come i campi di ricerca / discesa.

Il pessimismo è che lo sviluppatore ha ritenuto che le chiamate ripetute al servizio Web sarebbero state troppo lente per eseguire query SQL ripetute. Quindi cosa ha fatto? L'evento di avvio dell'applicazione legge nell'intero database e lo converte in oggetti in memoria, archiviati indefinitamente fino a quando il pool di app non viene riciclato. Questo codice era così lento che il caricamento in meno di 2000 dipendenti avrebbe richiesto 15 minuti. Se inavvertitamente hai riciclato il pool di app durante il giorno, potrebbero essere necessari almeno 30 minuti, poiché ogni richiesta del servizio Web avvierebbe più ricariche simultanee. Per questo motivo, i nuovi assunti non appariranno nel database il primo giorno in cui il loro account è stato creato e quindi non sarebbero in grado di accedere alla maggior parte delle app interne nei loro primi due giorni, facendo girare i pollici.

Il secondo livello di pessimismo è che il responsabile dello sviluppo non vuole toccarlo per paura di rompere le applicazioni dipendenti, ma tuttavia continuiamo ad avere sporadiche interruzioni a livello aziendale di applicazioni critiche a causa della cattiva progettazione di un componente così semplice.


28
Gestione ai massimi livelli: "No, non mettiamo 80 ore di programmazione una tantum a riparare questa app, è troppo costoso. Teniamola, così i suoi bug possono scaricare oltre 200 ore di utenti al mese, più 10 ore di programmatore al mese per 'Manutenzione'." AAAAAAAAAUGH !!!
Piskvor lasciò l'edificio il

25

Nessuno sembra aver menzionato l'ordinamento, quindi lo farò.

Diverse volte, ho scoperto che qualcuno aveva realizzato manualmente uno bolle, perché la situazione "non richiedeva" una chiamata all'algoritmo quicksort "troppo elaborato" che già esisteva. Lo sviluppatore è stato soddisfatto quando il suo bolle d'aria fatto a mano ha funzionato abbastanza bene sulle dieci file di dati che stanno usando per i test. Non è andato altrettanto bene dopo che il cliente ha aggiunto un paio di migliaia di righe.


2
L'ho fatto anch'io una volta, quando ho determinato che in genere n = 2. I successivi miglioramenti del prodotto hanno invalidato la mia premessa e il codice è stato sostituito PDQ.
Mark Ransom,

2
Sì, ma è bello scrivere qualcosa di algorio basato ogni tanto;)
UpTheCreek

20

Una volta ho lavorato su un'app che era piena di codice come questo:

 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 }

Semplicemente rimuovendo found, tornando nullalla fine e cambiando la sesta riga in:

            return curr;

Raddoppiato le prestazioni dell'app.


1
Una volta ho lavorato in un'azienda in cui le linee guida sulla codifica richiedevano "solo un ritorno alla fine" (per motivi di manutenzione). E in effetti un po 'di codice sputo come il tuo, perché non pensano (le soluzioni ovvie erano il più delle volte usando un goto all'uscita proc o cambiando la condizione di uscita dei loop)
flolo,

12
Un curr di ritorno qui produce un comportamento notevolmente diverso. Quando ritorni curr finisci per ottenere la PRIMA corrispondenza, dove come codice che hai incollato restituisce l'ultima corrispondenza.
SoapBox,

2
@SoapBox: hai ragione. @Dour High Arch: L'aumento delle prestazioni non ha nulla a che fare con la regola del ritorno singolo, poiché Flolo ha detto che l'inseguimento della condizione del loop (curr &&! Found) avrebbe avuto lo stesso effetto. GOTO all'uscita proc è orribile e sconfigge lo scopo della singola linea guida per il ritorno.
Akusete,

2
Buoni commenti a tutti. In questo caso si supponeva che ci fosse una sola tupla con ciascun ID.
Dour High Arch,

7
Non è una "pessimizzazione", vero? È semplicemente un'ottimizzazione in attesa di avvenire.
Tim Long,

20

Una volta ho dovuto tentare di modificare il codice che includeva queste gemme nella classe Costanti

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

Ognuno di questi è stato utilizzato più volte nel resto dell'applicazione per scopi diversi. COMMA_DELIMINATOR ha disseminato il codice di oltre 200 utilizzi in 8 pacchetti diversi.


Almeno qualcosa del genere è facile da trovare / sostituire dalla fonte - ancora, le mie simpatie.
Erik Forbes,

12
Inoltre - Deliminator? Ho pensato che fosse scritto "delimitatore". Deliminator sembra un brutto film di metà degli anni '90 che in qualche modo ha ottenuto 3 sequenze ...........
Erik Forbes,

53
Deliminator III: Rise of the Commas
Rob,

33
In un'altra nota, sono lieto di vedere la corretta delimitazione di Colins. Ogni programmatore degno di nota sa che se c'è una cosa che devi assolutamente separare correttamente, sono i dannati Colins.
Rob,

2
Non è così facile trovare e sostituire correttamente. Dal momento che ognuno è utilizzato per scopi diversi. Qualsiasi buon programmatore avrebbe almeno fatto qualcosa del genere: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... etc
KitsuneYMG il

19

Il grande numero uno di sempre in cui mi imbatto di volta in volta nel software interno:

Non utilizzare le funzionalità del DBMS per motivi di "portabilità" perché "potremmo voler passare a un altro fornitore in un secondo momento".

Leggi le mie labbra. Per qualsiasi lavoro interno: NON SARÀ ACCADUTO!


9
Succede MySQL -> postgresql, quindi non abbiamo perso nulla.
Thomas,

O postgres / postgis -> sqlite / spatialite ... Questo è stato un dolore nel culo ...
Philip

succede nei test JUnit
kachanov,

17

Avevo un collega che stava cercando di battere in astuzia l'ottimizzatore del nostro compilatore C e il codice di riscrittura di routine che solo lui poteva leggere. Uno dei suoi trucchi preferiti stava cambiando un metodo leggibile come (inventando un po 'di codice):

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;
}

in questo:

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

Cioè, la prima riga di un metodo leggibile una volta diventerebbe " return" e tutte le altre logiche verrebbero sostituite da espressioni ternarie profondamente annidate. Quando si tenta di discutere su come ciò non sia realizzabile, si indica che l'output dell'assemblaggio del suo metodo è stato di tre o quattro istruzioni di assemblaggio più brevi. Non era necessariamente più veloce, ma era sempre un po 'piccolo po 'più breve. Si trattava di un sistema incorporato in cui l'utilizzo della memoria occasionalmente contava, ma c'erano ottimizzazioni molto più semplici che avrebbero potuto essere fatte di così che avrebbe lasciato il codice leggibile.

Quindi, dopo questo, per qualche motivo ha deciso che ptr->structElementera troppo illeggibile, quindi ha iniziato a cambiarli tutti nella (*ptr).structElementteoria che era più leggibile e anche più veloce.

Trasformare il codice leggibile in codice illeggibile per un miglioramento massimo dell'1% e talvolta un codice più lento.


Se detto modulo viene chiamato milioni e milioni di volte per ciclo, allora approverei quell'ottimizzazione fintanto che ne commentasse il diavolo.
Michael Dorgan,

2
@Michael: Non lo farei, a meno che non ci fossero misurazioni che indicano che era più veloce , non solo più breve .
dsimcha,

Nella maggior parte dei casi l'operatore ternario è più leggibile di if. L'insistenza sulle dichiarazioni sulle espressioni in C è dogma culturale / religioso, non alcun tipo di pratica oggettiva. (Migliore linea guida: se il ternario nidificato è troppo lungo da leggere, non dovresti nemmeno usare if.)
Leushenko

2
Il problema qui è prendere un'intera funzione e sostituirla con una singola istruzione, un ritorno, sostituendo così tutta la logica dell'intera funzione con ternari nidificati. Se lo vedessi, capiresti. Questa non è una cosa religiosa "odio gli operatori ternari". Non sto parlando di prendere un singolo ifin una funzione e sostituirlo con un ternario. Va bene, e spesso più leggibile. Sto parlando di sostituire un intero metodo con oltre 30 righe con una singola istruzione return e ternari nidificati. Nessuno pensava che il nuovo codice fosse più leggibile, ma uno sviluppatore pensava che fosse più veloce.
Eddie,

15

In uno dei miei primi lavori come sviluppatore a pieno titolo, ho assunto un progetto per un programma che soffriva di problemi di ridimensionamento. Funzionerebbe ragionevolmente bene su insiemi di dati di piccole dimensioni, ma si bloccherebbe completamente quando vengono fornite grandi quantità di dati.

Mentre mi immergevo, ho scoperto che il programmatore originale ha cercato di velocizzare le cose parallelizzando l'analisi, lanciando un nuovo thread per ogni ulteriore fonte di dati. Tuttavia, aveva commesso un errore nel fatto che tutti i thread richiedevano una risorsa condivisa, su cui si stavano bloccando. Ovviamente, tutti i vantaggi della concorrenza sono scomparsi. Inoltre ha bloccato la maggior parte dei sistemi per avviare oltre 100 thread solo per bloccare tutti tranne uno. La mia potente macchina per gli sviluppatori è stata un'eccezione in quanto ha sfornato un set di dati di 150 fonti in circa 6 ore.

Quindi, per risolverlo, ho rimosso i componenti multi-threading e pulito l'I / O. Senza altre modifiche, il tempo di esecuzione sul set di dati di 150 origini è sceso sotto i 10 minuti sulla mia macchina e da infinito a meno di mezz'ora sulla macchina media dell'azienda.


Ho appena impedito che ciò accada in un progetto oggi. Ora so di aver fatto la scelta giusta.
deadalnix,

14

Suppongo di poter offrire questo gioiello:

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;
}

Dato che la radice quadrata è stata calcolata in un luogo molto sensibile, ho avuto il compito di cercare un modo per renderlo più veloce. Questo piccolo refactoring ha ridotto i tempi di esecuzione di un terzo (per la combinazione di hardware e compilatore utilizzati, 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;
}

Naturalmente ci sono entrambi modi più veloci E migliori per farlo, ma penso che sia un esempio piuttosto chiaro di pessimizzazione.

Modifica: vieni a pensarci bene, il ciclo srotolato era in realtà anche una pessimizzazione ordinata. Scavando attraverso il controllo della versione, posso presentare anche la seconda fase del refactoring, che ha funzionato anche meglio di quanto sopra:

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;
}

Questo è esattamente lo stesso algoritmo, anche se un'implementazione leggermente diversa, quindi suppongo che si qualifichi.


Suppongo che isqrt()calcoli floor(sqrt()), ma perché questo codice funziona?
Pablo H,

11

Questo potrebbe essere ad un livello superiore a quello che stavi cercando, ma risolverlo (se ti è permesso) comporta anche un livello più alto di dolore:

Insistere a portata di mano lanciando un Object Relationship Manager / Data Access Layer invece di utilizzare una delle librerie consolidate, testate e mature là fuori (anche dopo che ti sono state segnalate).


Non è sempre una cattiva idea creare il tuo codice. Come disse una volta un saggio, trova le dipendenze ed eliminale. Se è una funzione aziendale principale, fallo da solo.
Kibbee,

Non ho mai dedotto che sia sempre una cattiva idea. A meno che tu non dica Frans Bouma o simili, dubito che le cose ORM / DAL siano una funzione aziendale principale. È estremamente inefficace scrivere il proprio equivelante, un caso di reinventare la ruota (quadrata), di solito a causa della sindrome NIH.
Gordon Hartley,

@Kibbee - Sono d'accordo. È meglio creare il proprio e capirlo piuttosto che utilizzare dipendenze di terze parti. Quando si rompe (e lo farà) almeno allora puoi risolverlo. Ho trovato bug in Hibernate e Apache Commons in passato che stavano assolutamente uccidendo le prestazioni della nostra app.
CodingWithSpike

4
Lanciare a mano uno è davvero la tua unica opzione se nessuno di quelli affermati ha una funzionalità critica di cui hai bisogno.
staticsan

3
In realtà, dati alcuni dei commenti sopra, un po 'più di prospettiva: un'altra pessimizzazione è cercare di convincere l'ORM a fare assolutamente tutto. È spesso utile per il 95% + dei casi. Per quel 5% finale è molto più facile abbandonare il codice di persistenza elaborato a mano / chiamare direttamente le procedure memorizzate ecc. Per prestazioni, semplicità o entrambi.
Gordon Hartley,

10

Tutti i vincoli di chiave esterna sono stati rimossi da un database, perché altrimenti ci sarebbero così tanti errori.


8

Questo non corrisponde esattamente alla domanda, ma la citerò comunque una storia di ammonimento. Stavo lavorando su un'app distribuita che funzionava lentamente e sono volata a Washington per partecipare a una riunione principalmente volta a risolvere il problema. Il responsabile del progetto ha iniziato a delineare una nuova architettura volta a risolvere il ritardo. Mi sono offerto volontario di aver preso alcune misurazioni durante il fine settimana che hanno isolato il collo di bottiglia in un unico metodo. Si è scoperto che mancava un record in una ricerca locale, causando l'applicazione per passare a un server remoto su ogni transazione. Aggiungendo il record all'archivio locale, il ritardo è stato eliminato - problema risolto. Nota che la ri-architettura non avrebbe risolto il problema.


8

Verifica prima di OGNI operazione javascript se esiste l'oggetto su cui stai operando.

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
}

Il mio problema con questo tipo di codice è che nessuno sembra preoccuparsene se non esistesse? Non fare niente? Non dare il feedback all'utente?

Sono d'accordo che gli Object expectederrori sono fastidiosi, ma questa non è la soluzione migliore per questo.


Qual è la soluzione migliore allora? Penso che sia sciatto scrivere codice, in cui si verificano occasionalmente errori, anche se non hanno conseguenze dirette. Ovviamente non dovresti farlo, se non ti aspetti che l'oggetto sia nullo in nessun caso - forse questo è ciò che intendevi.
simon

7

Che ne dici dell'estremismo YAGNI. È una forma di pessimizzazione prematura. Sembra che ogni volta che applichi YAGNI, finisci per averne bisogno, con il risultato di 10 volte lo sforzo di aggiungerlo rispetto a quando lo avessi aggiunto all'inizio. Se crei un programma di successo, allora hai delle probabilità che LO STAI BISOGNO. Se sei abituato a creare programmi la cui vita si esaurisce rapidamente, allora continua a praticare YAGNI perché allora suppongo YAGNI.


3
Grazie, sono stufo di questi acuti acronimi di "programmazione estrema" e di come le persone li usano per sostenere pratiche pigre e controproducenti.
JAL

Gli studi di progetti reali mostrano che il fattore reale tra il codice di una volta e quello del riutilizzo è in media di circa 3. Quindi 10 è solo il valore "sentito", ma hai ragione.
Peter

@peterchen - stai dicendo che gli studi dimostrano che impiega tre volte più tempo per scrivere il codice riutilizzabile come un codice una tantum, o che mostrano che impiega tre volte più tempo per convertire il codice una tantum in codice riutilizzabile di quanto non faccia per scrivere il codice riutilizzabile in primo luogo?
Jeff Sternal,

@jeff: IIRC hanno confrontato alcune misure di complessità (qualunque cosa tu pensi di loro) di frammenti in linea che si sarebbero spostati su metodi separati. La complessità aumenta a causa di ulteriori casi supportati, controllo dei parametri ecc. (Il che mi fa supporre che i metodi fossero piuttosto piccoli). Vorrei provare a scavare un riferimento.
peterchen,

6

Non esattamente un'ottimizzazione prematura - ma certamente fuorviata - questo è stato letto sul sito web della BBC, da un articolo che parla di Windows 7.

Curran ha affermato che il team di Microsoft Windows ha studiato attentamente ogni aspetto del sistema operativo per apportare miglioramenti. "Siamo stati in grado di eliminare 400 millisecondi dal tempo di spegnimento tagliando leggermente la musica di arresto del file WAV.

Ora, non ho ancora provato Windows 7, quindi potrei sbagliarmi, ma sono disposto a scommettere che ci sono altri problemi che sono più importanti di quanto tempo ci vuole per spegnere. Dopotutto, una volta visualizzato il messaggio "Spegnimento di Windows", il monitor si spegne e me ne vado: come mi avvantaggiano quei 400 millisecondi?


Probabilmente scoprirai che gli altri problemi non sono così facili da spiegare ai non programmatori su un sito Web della BBC.
Tom Leys,

Questo è un angolo che non ho preso in considerazione - forse sto iniziando a perdere il mio cinismo :-)
belugabob,

Quel 400 ms è 400 ms di assorbimento di potenza. Probabilmente insignificante, ma forse si somma nel tempo. Tuttavia, non qualcosa di cui mi preoccuperei.
ZachS

1
Ho perso un sacco di ore in totale attesa dell'arresto delle macchine virtuali XP in modo da poter passare alla cosa successiva. Sono molto grato per uno spegnimento più rapido.
James,

1
È interessante notare che i file WAV vengono riprodotti in modo asincrono, quindi finché la fanfara di spegnimento è inferiore al tempo necessario per l'arresto, il taglio del file WAV non fa nulla. E ancora più interessante, se hanno ottimizzato l'arresto così tanto, come mai ogni scatola di Windows che ho bisogno ha bisogno di eoni fino a quando non è davvero inattivo? (Ad eccezione dell'utilizzo del grosso pulsante rosso, ovviamente.)
TheBlastOne

6

Qualcuno nel mio dipartimento una volta ha scritto una classe di stringhe. Un'interfaccia simile CString, ma senza la dipendenza di Windows.

Una "ottimizzazione" che hanno fatto è stata quella di non allocare più memoria del necessario. Apparentemente non si rende conto che la ragione per cui classi come std::stringallocare memoria in eccesso è tale che una sequenza di +=operazioni può essere eseguita in tempo O (n).

Invece, ogni singola +=chiamata ha forzato una riallocazione, che ha trasformato gli accodamenti ripetuti in un algoritmo O (n²) di Schlemiel il Pittore .


5

Un mio ex collaboratore (un soab , in realtà) è stato assegnato a costruire un nuovo modulo per il nostro ERP Java che avrebbe dovuto raccogliere e analizzare i dati dei clienti (settore della vendita al dettaglio). Decise di dividere OGNI campo Calendario / Datetime nei suoi componenti (secondi, minuti, ore, giorno, mese, anno, giorno della settimana, bimester, trimestre (!)) Perché "come altrimenti avrei cercato" ogni lunedì "?"


3
Non è un'ottimizzazione prematura, pensava di
doverlo

Certo, pensava di averne bisogno, ma poiché la maggior parte dei DBMS ha una sorta di funzione DAYOFWEEK (timestamp), fare quel pasticcio in anticipo è abbastanza prematuro secondo me :)
Joril,

1
Non lo userei per OLTP, ma se "analizzassi i dati dei clienti", questo è in realtà un modo molto flessibile di progettare un data warehouse (purché la data e l'ora siano divise in dimensioni diverse). Vorresti davvero chiamare DAYOFWEEK () su milioni di righe di dati o semplicemente fare una ricerca di un indice su un campo intero?
Tim Medora,

Beh, non so se ci fossero così tante file, ma sicuramente questa non è la spiegazione che è stata data :)
Joril

3

Senza offesa per nessuno, ma ho appena valutato un incarico (Java) che ha avuto questo

import java.lang.*;

1
A meno che questa non sia una classe di livello superiore, penso che tu debba tagliare un po 'questo studente a meno che non le abbia insegnato abbastanza per sapere perché questa non è una buona idea.
Bryan Oakley,

24
Sarò l'unico a notare l'ironia di un insegnante che chiama WTF sul codice di uno studente che è responsabile dell'insegnamento per programmare correttamente?
JohnFx,

3
Sì, non riesco a vedere che questo farebbe male. Nel peggiore dei casi è surpurfluous. Gli studenti tendono a ricorrere a una coerenza rigida mentre stanno imparando e l'importazione di java.lang è rigidamente coerente con ciò che lo studente ha imparato sull'importazione.
cygil,

1
Grazie a tutti per avermi detto l'ovvio. Era un incarico di Biologia Computazionale e non l'ho contato, né nemmeno menzionato.
sorvolato

2
@JohnFX: il selezionatore e l'insegnante non sono sempre la stessa persona.
Eddie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.