Qual é a pessimização mais ridícula que você já viu? [fechadas]

145

Todos sabemos que a otimização prematura é a raiz de todo mal, porque leva a um código ilegível / não-sustentável. Pior ainda é a pessimização, quando alguém implementa uma "otimização" porque acha que será mais rápida, mas acaba sendo mais lenta, além de ser de buggy, insustentável etc. Qual é o exemplo mais ridículo disso que você já viu ?

dsimcha
fonte
21
"Pessimização" é uma ótima palavra.
Mqp 26/03/09
Apenas no caso de você não saber, eles conversaram sobre o seu tópico aqui no podcast mais recente.
mmcdole

Respostas:

81

Em um projeto antigo, herdamos alguns programadores de sistemas embarcados (de outra forma excelentes) que possuíam uma enorme experiência com o Z-8000.

Nosso novo ambiente era o Sparc Solaris de 32 bits.

Um dos rapazes mudou todas as entradas para shorts para acelerar nosso código, já que pegar 16 bits da RAM era mais rápido do que pegar 32 bits.

Eu tive que escrever um programa de demonstração para mostrar que capturar valores de 32 bits em um sistema de 32 bits era mais rápido do que capturar valores de 16 bits e explicar que, para capturar um valor de 16 bits, a CPU precisava criar uma largura de 32 bits acesso à memória e, em seguida, oculte ou altere os bits não necessários para o valor de 16 bits.

Mark Harrison
fonte
16
Ei, onde você aprendeu sua matemática? 2 instruções com acesso a 1 cache / RAM são obviamente mais rápidas que 1 instrução com 1 acesso a cache / RAM!
Navalha Tempestade
2
@RazorStorm Em máquinas posteriores em que a largura de banda e o cache são mais preciosos, o oposto seria verdadeiro. O bitmask / shift é barato, mas você deseja ajustar o máximo possível no cache e também minimizar a largura de banda.
Jed
206

Eu acho que a frase "otimização prematura é a raiz de todo mal" é muito, muito usada. Para muitos projetos, tornou-se uma desculpa para não levar em consideração o desempenho até o final de um projeto.

Essa frase costuma ser uma muleta para as pessoas evitarem o trabalho. Eu vejo essa frase usada quando as pessoas realmente deveriam dizer "Nossa, nós realmente não pensamos nisso de antemão e não temos tempo para lidar com isso agora".

Eu já vi muitos exemplos "ridículos" de problemas de desempenho burros do que exemplos de problemas introduzidos devido à "pessimização"

  • Lendo a mesma chave do registro milhares (ou dezenas de milhares) de vezes durante o lançamento do programa.
  • Carregando a mesma DLL centenas ou milhares de vezes
  • Desperdiçando mega bytes de memória, mantendo caminhos completos para arquivos desnecessariamente
  • Não organizando estruturas de dados para que ocupem muito mais memória do que precisam
  • Dimensionando todas as strings que armazenam nomes ou caminhos de arquivos em MAX_PATH
  • Pesquisa gratuita para coisas que têm eventos, retornos de chamada ou outros mecanismos de notificação

O que eu acho que é uma afirmação melhor é a seguinte: "otimização sem medir e entender não é otimização - é apenas uma mudança aleatória".

Um bom trabalho de desempenho consome tempo - geralmente mais do que o desenvolvimento do recurso ou componente em si.

Foredecker
fonte
46
"Prematuro" é a palavra-chave dessa citação. Sua reformulação para "otimização sem medir e entender" não parece mudar nem um pouco o significado. Foi exatamente isso que Knuth quis dizer.
Bill the Lizard
13
@Foredecker: à direita. Muitas pessoas esquecem o contexto, o que coloca essa citação solidamente contra a micro- otimização. Analisar um problema para escolher o algoritmo adequado antes de implementá-lo não é prematuro, mas muitas vezes essa citação é exibida para justificar a solução mais preguiçosa e ineficiente.
Shog9
5
Ela realmente depende do caso individual, há mais casos onde a otimização prematura torna-se um problema de planeamento optimização inadequada torna-se um problema
Mark Rogers
12
-1: existe uma diferença entre "otimização" e design adequado. Para quem não sabe, uma boa regra é que uma "otimização" torna o código mais difícil de ler, mas mais rápido ou mais eficiente. Um design melhor tornará o código mais fácil de ler (ou pelo menos não pior) e mais eficiente.
TED
5
Se for muito usado, a população que faz perguntas sobre SO é fortemente ponderada em relação aos discrepantes. : D
dkretz 27/03/09
114

Bancos de dados são campos de pessimização.

Favoritos incluem:

  • Divida uma tabela em múltiplos (por período, intervalo alfabético etc.) porque é "muito grande".
  • Crie uma tabela de arquivamento para registros aposentados, mas continue a UNIÃO com a tabela de produção.
  • Duplicar bancos de dados inteiros por (divisão / cliente / produto / etc.)
  • Resista à adição de colunas a um índice porque o torna muito grande.
  • Crie muitas tabelas de resumo porque o recálculo dos dados brutos é muito lento.
  • Crie colunas com subcampos para economizar espaço.
  • Desnormalize em campos como uma matriz.

Isso está no topo da minha cabeça.

le dorfier
fonte
Resistir à necessidade de indexar é doloroso de se pensar.
Bill the Lizard
2
Sim, conheço alguém que trabalha para uma grande empresa petrolífera dos EUA, onde quase todas as suas tabelas têm uma tabela de arquivamento associada, e a maioria das consultas seleciona as visualizações que unificam os pares de tabelas. O desempenho é como você esperaria!
22430 Tony Andrews
Hah, acho que todo DBA deve ter descido a união com a rota da tabela de arquivamento em algum momento. Sempre parece tão razoável na época.
Cruachan
3
Eu acrescento: dividir o banco de dados em vários bancos de dados diferentes (clientes ac, os clientes DF, etc.)
Gabriele D'Antona
Você poderia elaborar "Desnormalizar em campos como uma matriz"? o que quer dizer aqui?
Bart van Heukelom
87

Acho que não existe uma regra absoluta: algumas coisas são melhor otimizadas antecipadamente e outras não.

Por exemplo, trabalhei em uma empresa em que recebemos pacotes de dados de satélites. Cada pacote custa muito dinheiro e, portanto, todos os dados são altamente otimizados (ou seja, compactados). Por exemplo, latitude / longitude não foi enviada como valores absolutos (flutuantes), mas como compensações em relação ao canto "noroeste" de uma zona "atual". Tivemos que descompactar todos os dados antes que pudessem ser usados. Mas acho que isso não é pessimização, é otimização inteligente para reduzir os custos de comunicação.

Por outro lado, nossos arquitetos de software decidiram que os dados descompactados deveriam ser formatados em um documento XML muito legível e armazenados em nosso banco de dados como tal (em vez de ter cada campo armazenado em uma coluna correspondente). A idéia deles era que "XML é o futuro", "espaço em disco é barato" e "processador é barato", portanto não havia necessidade de otimizar nada. O resultado foi que nossos pacotes de 16 bytes foram transformados em documentos de 2kB armazenados em uma coluna e, para consultas simples, tivemos que carregar megabytes de documentos XML na memória! Recebemos mais de 50 pacotes por segundo, para que você possa imaginar o quão horrível o desempenho se tornou (BTW, a empresa faliu).

Então, novamente, não há regra absoluta. Sim, às vezes a otimização muito cedo é um erro. Mas, às vezes, o lema "CPU / espaço em disco / memória é barato" é a verdadeira raiz de todo mal.

MiniQuark
fonte
37
Concordo que "CPU / espaço em disco / memória é barato" é a verdadeira raiz de todo mal. 1
ksuralta 26/03/09
5
Eu também ouvi esse desvio de XML. Outra empresa abastecida.
N8wrl 26/03/09
19
@ksuralta: "CPU / espaço em disco / memória é barato" é uma desculpa conveniente para evitar pensamentos. Evitar o pensamento é a raiz imaginária de todo o mal.
Piskvor saiu do prédio 28/03/2009
Essa XMLization também aconteceu no meu local de trabalho, seguida por JSONization. Tudo para evitar algum design de banco de dados relacional "trabalhoso".
precisa saber é o seguinte
75

Oh, bom Deus, acho que já vi todos eles. Na maioria das vezes, é um esforço para corrigir problemas de desempenho por alguém com muita preguiça de solucionar seus problemas até a CAUSA desses problemas de desempenho ou até mesmo pesquisar se há realmente um problema de desempenho. Em muitos desses casos, me pergunto se não é apenas o caso daquela pessoa querendo experimentar uma determinada tecnologia e procurando desesperadamente por uma unha que se encaixe no seu novo e brilhante martelo.

Aqui está um exemplo recente:

O arquiteto de dados me apresenta uma proposta elaborada para particionar verticalmente uma tabela-chave em um aplicativo bastante grande e complexo. Ele quer saber que tipo de esforço de desenvolvimento seria necessário para ajustar a mudança. A conversa foi assim:

Eu: Por que você está considerando isso? Qual é o problema que você está tentando resolver?

Ele: A Tabela X é muito ampla, estamos particionando por motivos de desempenho.

Eu: O que faz você pensar que é muito grande?

Ele: O consultor disse que são colunas demais para haver em uma tabela.

Eu: E isso está afetando o desempenho?

Ele: Sim, os usuários relataram lentidão intermitente no módulo XYZ do aplicativo.

Eu: Como você sabe que a largura da tabela é a fonte do problema?

Ele: Essa é a tabela principal usada pelo módulo XYZ, e tem 200 colunas. Deve ser o problema.

Eu (explicando): Mas o módulo XYZ, em particular, usa a maioria das colunas nessa tabela, e as colunas que ele usa são imprevisíveis porque o usuário configura o aplicativo para mostrar os dados que deseja exibir nessa tabela. É provável que 95% das vezes acabássemos juntando todas as tabelas novamente, o que prejudicaria o desempenho.

Ele: O consultor disse que é muito amplo e precisamos mudar isso.

Eu: Quem é esse consultor? Eu não sabia que contratamos um consultor, nem eles conversaram com a equipe de desenvolvimento.

Ele: Bem, ainda não os contratamos. Isso faz parte de uma proposta que eles ofereceram, mas insistiram que precisávamos re-arquitetar esse banco de dados.

Eu: Uh huh. Portanto, o consultor que vende serviços de redesenho de banco de dados acha que precisamos de um redesenho de banco de dados ....

A conversa continuou assim. Depois, dei uma outra olhada na tabela em questão e determinei que ela provavelmente poderia ser reduzida com alguma normalização simples, sem a necessidade de estratégias de particionamento exóticas. Isso, é claro, acabou sendo um ponto discutível depois que eu investiguei os problemas de desempenho (anteriormente não relatados) e os localizei em dois fatores:

  1. Índices ausentes em algumas colunas principais.
  2. Alguns analistas de dados não autorizados que estavam periodicamente bloqueando tabelas de chaves (incluindo a "ampla demais") consultando o banco de dados de produção diretamente com o MSAccess.

É claro que o arquiteto ainda está pressionando por um particionamento vertical da tabela pendurado no meta-problema "muito amplo". Ele até reforçou seu caso ao receber uma proposta de outro consultor de banco de dados que foi capaz de determinar que precisávamos de grandes alterações de design no banco de dados sem olhar o aplicativo ou executar qualquer análise de desempenho.

JohnFx
fonte
Aaag MSAccess to prod. Escrevemos um procedimento para descartar todas as conexões de acesso a cada poucos minutos, e finalmente chegamos à mensagem de que era ruim.
244/09 Nat
1
Tínhamos um trabalho semelhante, mas ele havia sido desuso. Para ser justo, o acesso não é o problema, apenas facilita para os neófitos criar / executar consultas sem desempenho.
31410 JohnFx
Temos uma dependência em nossa empresa em conexões de acesso ad hoc herdadas ao banco de dados de produção. Nada como alguns SQL'ers casuais para esquecer uma cláusula WHERE e travar as tabelas principais!
28909 HardCode
35
"Ouvi dizer que o malva tem mais memória RAM"
Piskvor deixou o prédio
Poderia ter sido pior. O Editor de Consultas do Excel bloqueia o banco de dados completo ao usá-lo. Quando não sabia, deixei uma instância aberta durante a maior parte do dia, enquanto trabalhava em outra coisa. A pior parte é que o MS SQL Server não informou o nome de usuário / máquina correto que estava fazendo o bloqueio. Percebi horas depois que eu era o motivo do bloqueio, porque as tabelas estavam bloqueadas sendo parte da exibição que eu estava consultando e tendo verificado tudo antes.
Esteban Küber
58

Eu já vi pessoas usando o alphadrive-7 para incubar totalmente o CHX-LT. Essa é uma prática incomum. A prática mais comum é inicializar o transformador ZT para reduzir a bufferização (devido à maior resistência à sobrecarga líquida) e criar bytegraphications no estilo java.

Totalmente pessimista!

Zalaga
fonte
10
talvez eles estavam tentando Embiggen o capacitor de fluxo
Mikeage
6
Então, basicamente, o único novo princípio envolvido é, em vez de a energia ser gerada pelo movimento relativo de condutores e fluxos, produzido pela interação modial da relutância magneto e da durabilidade capacitiva?
Matt Rogish
17
+1 porque o meu monitor necessária a limpeza de qualquer maneira ;-)
RBerteig
1
Droga!! Mas e o efeito ecletromagentic-cruz-genético. Eu acho que deve ser levado em consideração também. Ou o sujeito pode se transformar em um zumbi.
Suraj Chandran
1
Três palavras: "Mancais do silenciador cromado".
Allbite
53

Nada que abale a Terra, admito, mas já peguei pessoas usando o StringBuffer para concatenar Strings fora de um loop em Java. Era algo simples como virar

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

para dentro

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

Costumava ser uma prática bastante comum usar a técnica em loop, porque era mensurável mais rápido. O problema é que o StringBuffer é sincronizado, portanto, há sobrecarga extra se você estiver concatenando apenas algumas Strings. (Sem mencionar que a diferença é absolutamente trivial nessa escala.) Dois outros pontos sobre essa prática:

  1. StringBuilder não é sincronizado, portanto, deve ser preferido em relação a StringBuffer nos casos em que seu código não pode ser chamado de vários segmentos.
  2. Os compiladores Java modernos transformarão a concatenação de String legível em código de código otimizado para você quando for apropriado de qualquer maneira.
Bill, o Lagarto
fonte
3
Primeiro: Por que você não usaria pelo menos o Java 5? Segundo: Sim, você pode. Como você pode contar até 5 no primeiro exemplo, mas não no segundo? Ele usa os mesmos literais de String que o primeiro. Escreva um código legível e deixe o compilador decidir quando usar o StringBuffer nos bastidores.
Bill o Lagarto
4
@ MetroidFan2002: Os literais String no segundo exemplo também são objetos. Como eu disse na resposta, as diferenças são triviais nessa escala.
Bill o Lagarto
1
Isso não significa que substitua cada String por seu próprio StringBuffer. A otimização executada pelo compilador reduz o número de objetos criados.
Bill o Lagarto
3
@ Eric: String msg = "Contagem =" + contagem + "de" + total + "."; é frequentemente compilado em Java para String msg = new StringBuffer (). append ("Count"). append (count) .append ("of") .append (total) .append ("."). toString (); ... que é precisamente o que o segundo exemplo faz.
Grant Wagner
3
Sr. Wagner, a questão é que VOCÊ precisa examinar todas essas chamadas de método, não o compilador. Você precisa escrevê-los e compreendê-los mais tarde. O compilador faz o mesmo de qualquer maneira. Portanto, a legibilidade é mais importante neste caso.
ypnos
47

Uma vez vi um banco de dados MSSQL que usava uma tabela 'Root'. A tabela Raiz tinha quatro colunas: GUID (identificador exclusivo), ID (int), LastModDate (datetime) e CreateDate (datetime). Todas as tabelas no banco de dados foram chave estrangeira na tabela raiz. Sempre que uma nova linha foi criada em qualquer tabela no banco de dados, era necessário usar alguns procedimentos armazenados para inserir uma entrada na tabela Raiz antes de poder chegar à tabela real com a qual você se importava (em vez de o banco de dados fazer o trabalho para você com alguns gatilhos simples).

Isso criou uma bagunça inútil de ouvir e de cabeça, exigiu que qualquer coisa escrita em cima dela usasse sprocs (e eliminando minhas esperanças de apresentar o LINQ à empresa. Era possível, mas não merecia a dor de cabeça), e ainda por cima não ' nem realizar o que deveria fazer.

O desenvolvedor que escolheu esse caminho o defendeu sob a suposição de que isso economizava muito espaço porque não estávamos usando Guids nas próprias tabelas (mas ... não é um GUID gerado na tabela Raiz para todas as linhas que fazemos?) , melhorou o desempenho de alguma forma e tornou "fácil" auditar alterações no banco de dados.

Ah, e o diagrama do banco de dados parecia uma aranha mutante do inferno.

Dusda
fonte
42

E se pessimização do POBI obviamente por intenção?

Um colega meu nos anos 90 estava cansado de ser chutado pelo CEO apenas porque o CEO passou o primeiro dia de cada lançamento de software ERP (personalizado) com a localização de problemas de desempenho nas novas funcionalidades. Mesmo que as novas funcionalidades esmagassem gigabytes e tornassem o impossível possível, ele sempre encontrava alguns detalhes, ou mesmo um problema aparentemente importante, para reclamar. Ele acreditava saber muito sobre programação e se divertia chutando as bundas dos programadores.

Devido à natureza incompetente das críticas (ele era um CEO, não um cara de TI), meu colega nunca conseguiu acertar. Se você não tiver um problema de desempenho, não poderá eliminá-lo ...

Até um lançamento, ele colocou muitas chamadas de função Delay (200) (era Delphi) no novo código. Foram necessários apenas 20 minutos após a entrada em operação e ele recebeu ordem de comparecer no escritório do CEO para buscar pessoalmente seus insultos em atraso.

Até agora, a única coisa incomum que meus colegas ficaram mudos quando ele voltou, sorrindo, brincando, saindo para um BigMac ou dois, enquanto normalmente chutava mesas, brigava com o CEO e a empresa e passava o resto do dia morto. .

Naturalmente, meu colega agora descansava por um ou dois dias em sua mesa, aprimorando suas habilidades de mira no Quake - então, no segundo ou terceiro dia, ele excluiu as chamadas de atraso, reconstruiu e lançou um "patch de emergência" do qual espalhou a palavra que ele passou 2 dias e 1 noite para consertar os buracos no desempenho.

Esta foi a primeira (e única) vez em que o CEO do mal disse "ótimo trabalho!" para ele. Isso é tudo o que importa, certo?

Este era um verdadeiro POBI.

Mas também é um tipo de otimização de processos sociais, por isso é 100% ok.

Eu acho que.

TheBlastOne
fonte
10
Lembro-me de alguém escrevendo sobre um aplicativo de processamento de dados que foi vendido em diferentes níveis, em que o "Lite" podia reunir apenas alguns conjuntos de dados por segundo, a versão "superduper" milhares. A única diferença no código-fonte é o sono (N).
Peterchen
1
Brilhante! eu recomendaria esse padrão em uma situação como essa. No início do desenvolvimento, aloque uma grande parte da memória e adicione algumas chamadas de suspensão, e sempre que precisar obter algum desempenho, basta reduzi-las. É chamado de ser um trabalhador milagroso;)
RCIX
Infelizmente, o patch do Sleeps para os NOPs é fácil, para que a versão lite possa ser quebrada com muita facilidade. Essa reserva de "otimização" pode exigir um empacotador executável para dificultar a depuração e o patch.
TheBlastOne 17/08/10
32

"Independência do banco de dados". Isso significava que não havia procs armazenados, gatilhos, etc - nem mesmo chaves estrangeiras.

chris
fonte
8
É essa "independência" no sentido de que você está tão acima do banco de dados que esqueceu o que são dados? Abstrair desnecessariamente os bancos de dados "para evitar dores de migração" é irritante; você não vai precisar disso.
Rob
8
Bastante. Astronautas de arquitetura no trabalho. Desenvolvo aplicativos da Web desde que existia uma Web e, durante todo esse tempo, nunca mudei de uma plataforma de banco de dados para outra.
chris
5
Tenho certeza de que isso acontece, mas é raro o suficiente que você seja um idiota se projetar sua arquitetura em torno dessa possibilidade.
chris
6
Harpo, essa é uma situação diferente - é uma exigência nesse caso. Estou falando de quando não é um requisito, mas o AA decide que "pode ​​ser" em algum momento.
chris
3
@Todos: a independência do banco de dados pode custar, sim, mas nosso produto é executado em ambientes em que o fornecedor do banco de dados é escolhido por lances, e basicamente precisamos concordar. Alguns desenvolvedores não se dão ao luxo de uma pilha de software verticalmente integrada e precisam se contentar apesar disso.
Chris R
31
var stringBuilder = new StringBuilder();
stringBuilder.Append(myObj.a + myObj.b + myObj.c + myObj.d);
string cat = stringBuilder.ToString();

Melhor uso de um StringBuilder que eu já vi.

luft
fonte
9
Fale sobre "pouco claro sobre o conceito"! Uau!
Eddie
3
Legal. "Meu líder diz que tenho que usar a classe StringBuilder se quiser concatenar seqüências de caracteres. É isso que eu faço. Então, o que há de errado?" Lol ...
TheBlastOne
26

Usando um regex para dividir uma string quando um simples string.split é suficiente

Cherian
fonte
25
MAS no Java String.Split usa um regex!
31430 Frank Krueger
Não vejo como um Regex poderia ser tão rápido quanto uma divisão de cadeia interna.
Andrei Rînea 26/04/09
2
Mas caçar deliberadamente um regex usado para dividir seqüências e substituí-lo por uma função de divisão "simples" soa como um exemplo perfeito de pessimização. As bibliotecas Regex são rápidas o suficiente.
21430 David Crawshaw
5
@ David Crawshaw: caçar oportunidades de micro-otimização desperdiça tempo humano; Quando escrever código, use a solução suficiente menos complexa.
Piskvor saiu do prédio 25/08/10
6
-1: se você está acostumado a expressões regulares, é muito natural escrever isso em vez de se acostumar com os manipuladores de string internos do idioma 1001.
precisa saber é o seguinte
26

Muito tarde para esta discussão, eu sei, mas vi isso recentemente:

bool isFinished = GetIsFinished();

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

    case false:
        DoNextStep();
        break;

    default:
        DoNextStep();
}

Sabe, no caso de um booleano ter alguns valores extras ...

Damovisa
fonte
22
É claro, falso e um FileNotFound ofcourse
Ikke
Ei, você sempre deve ter um padrão / case else / etc. O que acontece quando uma pessoa brilhante muda esse valor booleano para uma enum para refletir outro status, então a próxima pessoa adiciona à enum e esquece de modificar o procedimento? Ter um padrão quando você não precisa gastar tempo de execução e muito pouco tempo de desenvolvimento. Rastrear um erro lógico acidentalmente introduzido que ocorre durante o tempo de execução ... Isso custa tempo, dinheiro e reputação. Um ponto no tempo salva nove.
Oorang
1
@ Orang ... por que você teria isso como um interruptor de qualquer maneira? É um boleano - um if / else é tudo o que é necessário.
Damovisa 18/03/10
@Damovisa facepalm direita ... muito bem, então :) perdidas que :)
Oorang
2
Foi Nullable <Boolean> ... :)
George Chakhidze
25

O pior exemplo que posso imaginar é um banco de dados interno da minha empresa que contém informações sobre todos os funcionários. Ele recebe uma atualização noturna do RH e tem um serviço da Web ASP.NET na parte superior. Muitos outros aplicativos usam o serviço da web para preencher itens como campos de pesquisa / lista suspensa.

O pessimismo é que o desenvolvedor pensou que as chamadas repetidas para o serviço da web seriam muito lentas para fazer consultas SQL repetidas. então, o que ele fez? O evento de início do aplicativo lê todo o banco de dados e converte tudo em objetos na memória, armazenados indefinidamente até que o pool de aplicativos seja reciclado. Esse código era tão lento que levaria 15 minutos para carregar menos de 2000 funcionários. Se você inadvertidamente reciclou o pool de aplicativos durante o dia, isso poderia levar 30 minutos ou mais, porque cada solicitação de serviço da Web iniciaria várias recargas simultâneas. Por esse motivo, novas contratações não apareceriam no banco de dados no primeiro dia em que sua conta foi criada e, portanto, não poderiam acessar a maioria dos aplicativos internos nos primeiros dois dias, mexendo nos polegares.

O segundo nível de pessimismo é que o gerente de desenvolvimento não deseja tocá-lo por medo de quebrar aplicativos dependentes, mas ainda continuamos a ter interrupções esporádicas de aplicativos críticos em toda a empresa devido ao mau design de um componente tão simples.

Spoulson
fonte
28
Gerenciamento da melhor forma possível: "Não, não vamos colocar 80 horas de programador únicas para consertar esse aplicativo, isso é muito caro. Vamos mantê-lo, para que seus erros possam esgotar mais de 200 horas de usuário por mês, mais 10 horas de programador por mês durante 'manutenção'." AAAAAAAAAUGH !!!
Piskvor saiu do prédio 28/03/2009
25

Parece que ninguém mencionou a classificação, então eu irei.

Várias vezes diferentes, descobri que alguém havia criado uma bolha de bolhas, porque a situação "não exigia" uma chamada para o algoritmo de classificação rápida "muito sofisticado" que já existia. O desenvolvedor ficou satisfeito quando sua bolha artesanal trabalhou bem o suficiente nas dez linhas de dados que eles estavam usando para teste. Não foi tão bom assim depois que o cliente adicionou algumas milhares de linhas.

Dan Breslau
fonte
2
Eu fiz isso sozinho uma vez, quando determinei que normalmente n = 2. Os aprimoramentos posteriores do produto invalidaram minha premissa e o código foi substituído pelo PDQ.
Mark Ransom
2
Sim, mas é bom escrever algo baseado em algoritmo de vez em quando;)
UpTheCreek 27/07/10
20

Certa vez, trabalhei em um aplicativo cheio 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 }

Basta remover found, retornar nullno final e alterar a sexta linha para:

            return curr;

Duplicou o desempenho do aplicativo.

Arco Alto Dour
fonte
1
Eu trabalhei uma vez em uma empresa onde as diretrizes de codificação exigiam "apenas um retorno no final" (para manutenção). E, de fato algum código cuspir como o seu, porque não acho que (as soluções óbvias era na maioria das vezes usando um goto para a saída proc, ou mudando a condição de saída de loops)
flolo
12
Uma corrente de retorno aqui produz um comportamento notavelmente diferente. Quando você retorna ao curr, acaba recebendo a PRIMEIRA correspondência, onde, como o código que você colou, retorna a ÚLTIMA correspondência.
SoapBox 28/11/08
2
@ SoapBox: Você está certo. @ High Arch: O aumento no desempenho não teve nada a ver com a regra de retorno único, pois flolo disse que perseguir a condição do loop para (curr &&! Found) teria o mesmo efeito. GOTO para a saída do processo é horrível e derrota o objetivo da diretriz de retorno único.
Akusete 28/11/08
2
Bons comentários a todos. Nesse caso, deveria haver apenas uma única tupla com cada ID.
Arco Alto de Dour
7
Isso não é uma "pessimização", é? É simplesmente uma otimização esperando para acontecer.
Tim Long
20

Uma vez eu tive que tentar modificar o código que incluía essas gemas na classe Constants

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

Cada um deles foi usado várias vezes no restante do aplicativo para diferentes propósitos. COMMA_DELIMINATOR encheu o código com mais de 200 usos em 8 pacotes diferentes.

KitsuneYMG
fonte
Pelo menos algo assim é fácil de encontrar / substituir fora da fonte - ainda assim, minhas simpatias.
31411 Erik Forbes
12
Também - Deliminador? Eu pensei que estava escrito 'delimitador'. Sons deliminator como um filme de meados dos anos 90. ruim que de alguma forma tem 3 Sequals ...........
Erik Forbes
53
Deliminator III: Rise of the vírgulas
Rob
33
Em outra nota, tenho o prazer de ver a delimitação adequada de Colins. Todo programador que se preze sabe que, se há uma coisa que você absolutamente deve separar adequadamente, são os malditos Colins.
2626 Rob
2
Não é tão fácil encontrar e substituir adequadamente. Uma vez que cada um é usado para diferentes fins. Qualquer bom programador teria feito pelo menos algo assim: COUNTRY_LIST_DELIM = ... CLASSIFICATION_DELIM = ... etc #
KitsuneYMG 26/09/09
19

O maior número um de todos os tempos, que repito várias vezes no software interno:

Não uso dos recursos do DBMS por motivos de "portabilidade" porque "podemos querer mudar para outro fornecedor posteriormente".

Leia meus lábios. Para qualquer trabalho interno: NÃO ACONTECE!

Peter Stuer
fonte
9
Isso acontece. MySQL -> postgresql, então não perdemos nada.
Thomas
Ou PostgreSQL / PostGIS -> sqlite / SpatiaLite ... Isso foi um pé no saco ...
Philip
isso acontece em testes JUnit
Kachanov
17

Eu tinha um colega de trabalho que estava tentando superar o otimizador de nosso compilador C e reescrever o código de rotina que somente ele poderia ler. Um de seus truques favoritos era mudar um método legível como (criando algum 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;
}

nisso:

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

Ou seja, a primeira linha de um método que antes era legível se tornaria " return" e toda a outra lógica seria substituída por expressões ternárias profundamente aninhadas. Quando você tentava argumentar sobre como isso era impossível de manter, ele apontava para o fato de que a saída do método de montagem era três ou quatro instruções de montagem mais curtas. Não era necessariamente qualquer mais rápido , mas ele sempre foi um minúsculo pouco mais curto. Este era um sistema embutido onde o uso de memória ocasionalmente importava, mas havia otimizações muito mais fáceis que poderiam ter sido feitas do que aquelas que deixariam o código legível.

Depois disso, por algum motivo, ele decidiu que ptr->structElementera ilegível demais, então começou a mudar tudo isso para(*ptr).structElement a teoria de que era mais legível e mais rápido também.

Transformar código legível em código ilegível para uma melhoria máxima de 1% e, às vezes, de fato, um código mais lento.

Eddie
fonte
Se o referido módulo estiver sendo chamado milhões e milhões de vezes por loop, eu aprovaria essa otimização, desde que ele comentasse a questão.
Michael Dorgan
2
@ Michael: Eu não faria, a menos que houvesse medidas indicando que era mais rápido , não apenas mais curto .
dsimcha
Na maioria das situações, o operador ternário é mais legível que if. A insistência em declarações sobre expressões em C é um dogma cultural / religioso, e não qualquer tipo de prática objetiva. (Melhor orientação: se o ternário aninhado for muito longo para ser lido, você não deve usarif qualquer um.)
Leushenko
2
O problema aqui é pegar uma função inteira e substituí-la por uma única instrução, um retorno, substituindo assim toda a lógica de toda a função por ternários aninhados. Se você visse, entenderia. Isso não é algo religioso "Eu odeio operadores ternários". Não estou falando de pegar um único ifem uma função e substituí-lo por um ternário. Tudo bem, e muitas vezes mais legível. Estou falando de substituir um método inteiro de mais de 30 linhas por uma única declaração de retorno e ternários aninhados. Ninguém achou que o novo código era mais legível, mas um desenvolvedor achou que era mais rápido.
Eddie
15

Em um dos meus primeiros trabalhos como desenvolvedor de pleno direito, assumi o projeto de um programa que estava sofrendo problemas de dimensionamento. Funcionaria razoavelmente bem em pequenos conjuntos de dados, mas travaria completamente quando recebesse grandes quantidades de dados.

Ao pesquisar, descobri que o programador original procurava acelerar as coisas paralelizando a análise - lançando um novo encadeamento para cada fonte de dados adicional. No entanto, ele cometera um erro, pois todos os threads exigiam um recurso compartilhado, no qual estavam em conflito. Obviamente, todos os benefícios da concorrência desapareceram. Além disso, ele travou a maioria dos sistemas ao lançar mais de 100 threads apenas para bloquear todos, exceto um. Minha máquina de desenvolvimento robusta era uma exceção, pois agitava um conjunto de dados de 150 fontes em cerca de 6 horas.

Portanto, para corrigi-lo, removi os componentes com vários threads e limpei a E / S. Sem outras alterações, o tempo de execução no conjunto de dados de 150 fontes caiu abaixo de 10 minutos na minha máquina e do infinito para menos de meia hora na máquina média da empresa.

Jeffrey Blake
fonte
Eu apenas evito que isso aconteça em um projeto hoje. Agora eu sei que fiz a boa escolha.
Deadalnix #
14

Suponho que eu poderia oferecer esta jóia:

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 a raiz quadrada foi calculada em um local muito sensível, tive a tarefa de procurar uma maneira de torná-la mais rápida. Essa pequena refatoração reduziu o tempo de execução em um terço (para a combinação de hardware e compilador usado, 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;
}

É claro que existem maneiras mais rápidas e melhores de fazer isso, mas acho que é um exemplo bastante interessante de pessimização.

Edit: Venha para pensar sobre isso, o loop desenrolado também era na verdade uma pessimização pura. Indo além do controle de versão, também posso apresentar o segundo estágio da refatoração, que teve um desempenho ainda melhor do que o descrito acima:

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 é exatamente o mesmo algoritmo, embora uma implementação ligeiramente diferente, então suponho que seja qualificado.

Christoffer
fonte
Suponho que isqrt()calcule floor(sqrt()), mas, por que esse código funciona?
21718 Pablo H:
11

Isso pode estar em um nível mais alto do que o que você buscava, mas corrigi-lo (se você puder) também envolve um nível mais alto de dor:

Insistindo na rolagem manual de um Gerenciador de Relacionamento com Objetos / Camada de Acesso a Dados, em vez de usar uma das bibliotecas estabelecidas, testadas e maduras existentes no mercado (mesmo depois de indicadas a você).

Gordon Hartley
fonte
Nem sempre é uma má ideia lançar seu próprio código. Como um homem sábio disse uma vez, encontre as dependências e elimine-as. Se é uma função principal do negócio, faça você mesmo.
Kibbee
Eu nunca deduzi que é sempre uma má ideia. A menos que você diga Frans Bouma ou similar, duvido que o material ORM / DAL seja uma função principal do negócio. É extremamente ineficaz escrever seu próprio equivalente, um caso de reinventar a roda (quadrada), geralmente devido à síndrome do NIH.
Gordon Hartley
@Kibbee - eu concordo. É melhor criar o seu próprio e entendê-lo do que usar dependências de terceiros. Quando ele quebra (e vai), pelo menos você pode consertá-lo. Encontrei bugs no Hibernate e Apache Commons no passado que estavam absolutamente prejudicando o desempenho do nosso aplicativo.
CodingWithSpike
4
Rolar manualmente é realmente sua única opção se nenhum dos estabelecidos tiver um recurso crítico que você precisa.
staticsan
3
Na verdade, considerando alguns dos comentários acima, mais algumas perspectivas: outra pessimização é tentar fazer com que o ORM faça absolutamente tudo. Geralmente é útil para mais de 95% dos casos. Para esses 5% finais, é muito mais fácil usar códigos de persistência criados manualmente / chamadas de procedimento armazenado direto etc. para desempenho, simplicidade ou ambos.
Gordon Hartley
10

Todas as restrições de chave estrangeira foram removidas de um banco de dados, porque, caso contrário, haveria muitos erros.

Guge
fonte
8

Isso não se encaixa exatamente na questão, mas mencionarei de qualquer maneira uma história de advertência. Eu estava trabalhando em um aplicativo distribuído que estava sendo executado lentamente e voei até o DC para participar de uma reunião destinada principalmente a resolver o problema. O líder do projeto começou a esboçar uma re-arquitetura destinada a resolver o atraso. Ofereci que havia realizado algumas medições no fim de semana que isolaram o gargalo em um único método. Aconteceu que havia um registro ausente em uma pesquisa local, fazendo com que o aplicativo tivesse que ir para um servidor remoto em todas as transações. Ao adicionar o registro de volta à loja local, o atraso foi eliminado - problema resolvido. Observe que a re-arquitetura não teria resolvido o problema.

Jack BeNimble
fonte
8

Verificando antes de CADA operação javascript se o objeto em que você 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
}

Meu problema com esse tipo de código é que ninguém parece se importar, e se ele não existir? Apenas não faz nada? Não dê o feedback ao usuário?

Concordo que os Object expectederros são irritantes, mas esta não é a melhor solução para isso.

Chetan Sastry
fonte
Qual é a melhor solução então? Eu acho que é desleixado escrever código, onde erros ocorrem ocasionalmente, mesmo que não tenham conseqüências diretas. É claro que você não deve fazê-lo, se não espera que o objeto seja nulo em nenhuma circunstância - talvez seja isso que você quis dizer.
simon
7

E o extremismo YAGNI? É uma forma de pessimização prematura. Parece que sempre que você aplica o YAGNI, acaba precisando dele, resultando em 10 vezes mais esforço para adicioná-lo do que se você o tivesse adicionado no início. Se você criar um programa bem-sucedido, é provável que VOCÊ PRECISA NECESSÁRIO. Se você está acostumado a criar programas cuja vida se esgota rapidamente, continue praticando o YAGNI porque, suponho, o YAGNI.

Dunk
fonte
3
Obrigado, estou cansado desses acrônimos de 'programação extrema' e como as pessoas os usam para apoiar práticas preguiçosas e contraproducentes.
JAL
Estudos de projetos reais mostram que o fator real entre o código único e o código de reutilização é em média de cerca de 3. Portanto, 10 é apenas o valor "sentido", mas você está certo por intenção.
Peterchen
@peterchen - você está dizendo que estudos mostram que leva três vezes mais tempo para escrever código reutilizável que código único ou que eles mostram que leva três vezes mais tempo para converter código único em código reutilizável do que escrever o código reutilizável em primeiro lugar?
Jeff Sternal 29/07
@jeff: o IIRC compararam alguma medida de complexidade (o que você acha deles) de trechos em linha que foram movidos para métodos separados. A complexidade aumenta devido a casos adicionais suportados, verificação de parâmetros etc. (o que me faz supor que os métodos eram bastante pequenos). Deixe-me tentar descobrir uma referência.
peterchen
6

Não é exatamente uma otimização prematura - mas certamente equivocada - isso foi lido no site da BBC, em um artigo que discute o Windows 7.

Curran disse que a equipe do Microsoft Windows examinou todos os aspectos do sistema operacional para fazer melhorias. "Conseguimos reduzir 400 milissegundos do tempo de desligamento cortando levemente a música de desligamento de arquivos WAV.

Agora, ainda não testei o Windows 7, por isso posso estar errado, mas estou disposto a apostar que existem outros problemas que são mais importantes do que o tempo que leva para desligar. Afinal, quando vejo a mensagem 'Desligando o Windows', o monitor é desligado e eu vou embora - como esses 400 milissegundos me beneficiam?

belugabob
fonte
Você provavelmente descobrirá que os outros problemas não são tão fáceis de explicar aos não programadores em um site da BBC.
5309 Tom Leys
Agora que é um ângulo que eu não considero - talvez eu estou começando a perder meu cinismo :-)
belugabob
Esses 400 ms são 400 ms de consumo de energia. Provavelmente insignificante, mas talvez isso aconteça com o tempo. Ainda assim, não é algo que eu me preocuparia.
ZachS 02/10/2009
1
Perdi muitas horas no total, esperando que as VMs do XP desligassem, para que eu possa passar para a próxima coisa. Sou muito grato por um desligamento mais rápido.
James
1
Curiosamente, os arquivos WAV são reproduzidos de forma assíncrona, portanto, desde que a fanfarra de desligamento seja menor que o tempo necessário para desligar, a remoção do arquivo WAV não fará nada. E ainda mais interessante, se eles otimizaram muitíssimo o desligamento, como é que todas as caixas que eu desligo do Windows precisam de eons até que realmente desapareça? (Exceto para usando o botão vermelho grande, é claro.)
TheBlastOne
6

Alguém do meu departamento escreveu uma aula de cordas. Uma interface como CString, mas sem a dependência do Windows.

Uma "otimização" que eles fizeram foi não alocar mais memória do que o necessário. Aparentemente, não percebendo que o motivo pelo qual as classes std::stringalocam excesso de memória é para que uma sequência de +=operações possa ser executada em O (n) tempo.

Em vez disso, todas as +=chamadas forçavam uma realocação, que transformava anexos repetidos em um algoritmo de O (n²) Schlemiel, o Pintor .

dan04
fonte
5

Um ex-colega de trabalho meu (um sabão , na verdade) foi designado para criar um novo módulo para o nosso Java ERP que deveria ter coletado e analisado os dados dos clientes (setor de varejo). Ele decidiu dividir TODOS os campos Calendário / Data e hora em seus componentes (segundos, minutos, horas, dia, mês, ano, dia da semana, bimestre, trimestre (!)) Porque "de que outra forma eu procuraria por 'toda segunda-feira'?"

Joril
fonte
3
Isso não é uma otimização prematura, ele achava que ele precisava fazer isso para correção
Pyrolistical
Claro, ele pensou que ele precisava disso, mas desde que a maioria DBMSes ter algum tipo de função DAYOFWEEK (timestamp), fazendo aquela bagunça up-front é suficiente prematura em minha opinião :)
Joril
1
Eu não o usaria para OLTP, mas se você estivesse "analisando os dados do cliente", essa seria realmente uma maneira muito flexível de projetar um data warehouse (desde que a data e a hora sejam divididas em diferentes dimensões). Deseja realmente chamar DAYOFWEEK () contra milhões de linhas de dados ou apenas procurar um índice em um campo inteiro?
Tim Medora
Bem, eu não sei se houve tanto linhas, mas certamente isso não é a explicação que foi dada :)
Joril
3

Sem ofender ninguém, mas acabei de classificar uma tarefa (java) que tinha essa

import java.lang.*;
Sobrecarregado
fonte
1
A menos que seja uma classe de nível superior, acho que você precisa diminuir um pouco essa aluna, a menos que a ensine o suficiente para saber por que essa não é uma boa ideia.
22909 Bryan Oakley
24
Serei o único a notar a ironia de um professor ligando para a WTF no código de um aluno que ele / ela é responsável por ensinar a programar corretamente?
31409 JohnFx
3
Sim, não vejo que isso dói. Na pior das hipóteses, é surpurfluous. Os alunos tendem a recorrer a uma consistência rígida enquanto aprendem, e a importação de java.lang é rigidamente consistente com o que o aluno aprendeu sobre importação.
cygil
1
Obrigado a todos por me dizerem o óbvio. Era uma tarefa de Biologia Computacional e eu não contei, nem sequer mencionei.
sobrevoado
2
@ JohnFX: O aluno e o professor nem sempre são a mesma pessoa.
Eddie