OK, você está definindo o problema para onde parece que não há muito espaço para melhorias. Isso é bastante raro, na minha experiência. Tentei explicar isso em um artigo do Dr. Dobbs em novembro de 1993, partindo de um programa não trivial convencionalmente bem projetado, sem desperdício óbvio, e conduzindo-o através de uma série de otimizações até que o tempo do relógio de parede fosse reduzido de 48 segundos para 1,1 segundos, e o tamanho do código-fonte foi reduzido em um fator de 4. Minha ferramenta de diagnóstico foi essa . A sequência das mudanças foi a seguinte:
O primeiro problema encontrado foi o uso de clusters de lista (agora chamados de "iteradores" e "classes de contêineres"), responsáveis por mais da metade do tempo. Eles foram substituídos por um código bastante simples, reduzindo o tempo para 20 segundos.
Agora, o maior tomador de tempo é mais construção de lista. Como porcentagem, não era tão grande antes, mas agora é porque o problema maior foi removido. Eu encontro uma maneira de acelerar, e o tempo cai para 17 segundos.
Agora é mais difícil encontrar culpados óbvios, mas existem alguns menores sobre os quais posso fazer algo, e o tempo cai para 13 segundos.
Agora eu pareço ter atingido uma parede. As amostras estão me dizendo exatamente o que está fazendo, mas não consigo encontrar nada que possa melhorar. Depois, reflito sobre o design básico do programa, sobre sua estrutura orientada a transações e pergunto se toda a pesquisa de lista que ele está fazendo é realmente exigida pelos requisitos do problema.
Então, refiz um novo design, onde o código do programa é realmente gerado (por meio de macros do pré-processador) a partir de um conjunto menor de fontes, e no qual o programa não está constantemente descobrindo coisas que o programador sabe que são bastante previsíveis. Em outras palavras, não "interprete" a sequência de coisas a fazer, "compile".
- Essa reformulação é feita, diminuindo o código-fonte por um fator de 4 e o tempo é reduzido para 10 segundos.
Agora, porque está ficando tão rápido, é difícil fazer uma amostra, por isso dou 10 vezes mais trabalho a fazer, mas os seguintes são baseados na carga de trabalho original.
Mais diagnóstico revela que está gastando tempo no gerenciamento de filas. O alinhamento destes reduz o tempo para 7 segundos.
Agora, um grande tomador de tempo é a impressão de diagnóstico que eu estava fazendo. Lave isso - 4 segundos.
Agora, os maiores tomadores de tempo são chamadas para malloc e grátis . Reciclar objetos - 2,6 segundos.
Continuando a amostra, ainda encontro operações que não são estritamente necessárias - 1,1 segundos.
Fator de aceleração total: 43,6
Agora, não existem dois programas iguais, mas em softwares que não são de brinquedos, sempre vi uma progressão como essa. Primeiro você obtém as coisas fáceis e depois as mais difíceis, até chegar a um ponto de retornos decrescentes. Então, a percepção que você obtém pode muito bem levar a uma reformulação, iniciando uma nova rodada de acelerações, até que você atinja novamente retornos decrescentes. Agora, este é o ponto em que pode fazer sentido se perguntar se ++i
ou i++
ou for(;;)
ou while(1)
são mais rápidos: os tipos de perguntas que eu vejo tantas vezes no Stack Overflow.
PS Pode-se perguntar por que não usei um criador de perfil. A resposta é que quase todos esses "problemas" eram um site de chamada de função, que empilha exemplos de maneira precisa. Ainda hoje, os criadores de perfil estão apenas chegando à ideia de que instruções e instruções de chamada são mais importantes para localizar e mais fáceis de corrigir do que funções inteiras.
Na verdade, eu criei um criador de perfil para fazer isso, mas para uma verdadeira intimidade com o que o código está fazendo, não há substituto para acertar os dedos. Não é um problema que o número de amostras seja pequeno, porque nenhum dos problemas encontrados é tão pequeno que é facilmente esquecido.
ADICIONADO: jerryjvl solicitou alguns exemplos. Aqui está o primeiro problema. Consiste em um pequeno número de linhas de código separadas, que levam mais de metade do tempo:
/* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)
Eles estavam usando o cluster de lista ILST (semelhante a uma classe de lista). Eles são implementados da maneira usual, com "ocultação de informações", significando que os usuários da classe não deveriam ter que se importar com como eles foram implementados. Quando essas linhas foram escritas (de aproximadamente 800 linhas de código), não se pensou na idéia de que elas poderiam ser um "gargalo" (eu odeio essa palavra). Eles são simplesmente a maneira recomendada de fazer as coisas. É fácil dizer, em retrospectiva, que eles deveriam ter sido evitados, mas, na minha experiência, todos os problemas de desempenho são assim. Em geral, é bom tentar evitar a criação de problemas de desempenho. É ainda melhor encontrar e consertar os que são criados, mesmo que "devam ter sido evitados" (em retrospectiva).
Aqui está o segundo problema, em duas linhas separadas:
/* ADD TASK TO TASK LIST */
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)
Essas são listas de construção anexando itens a seus fins. (A correção foi coletar os itens em matrizes e criar as listas de uma só vez.) O interessante é que essas instruções custam apenas (ou seja, estavam na pilha de chamadas) 3/48 do tempo original, portanto, não estavam dentro fato um grande problema no começo . No entanto, depois de remover o primeiro problema, eles custaram 3/20 do tempo e agora eram um "peixe maior". Em geral, é assim que acontece.
Devo acrescentar que este projeto foi destilado de um projeto real no qual ajudei. Nesse projeto, os problemas de desempenho eram muito mais dramáticos (assim como as acelerações), como chamar uma rotina de acesso ao banco de dados dentro de um loop interno para verificar se uma tarefa foi concluída.
REFERÊNCIA ADICIONADA: O código fonte, original e reprojetado, pode ser encontrado em www.ddj.com , para 1993, no arquivo 9311.zip, nos arquivos slug.asc e slug.zip.
EDIT 26/11/2011: Agora existe um projeto SourceForge contendo código-fonte no Visual C ++ e uma descrição detalhada de como foi ajustada. Ele passa apenas pela primeira metade do cenário descrito acima e não segue exatamente a mesma sequência, mas ainda recebe uma aceleração de ordem de magnitude 2-3.
Sugestões:
Desvantagens : se alguns dos valores pré-calculados forem realmente usados, isso pode piorar as coisas, também a pesquisa pode exigir memória significativa.
Pontos negativos : escrever código adicional significa mais área de superfície para bugs.
Desvantagens : Bem ... a resposta não será exata.
fonte
Quando você não puder mais melhorar o desempenho - veja se pode melhorar o desempenho percebido .
Talvez você não consiga acelerar o algoritmo fooCalc, mas muitas vezes existem maneiras de fazer com que seu aplicativo pareça mais responsivo ao usuário.
Alguns exemplos:
Isso não tornará seu programa mais rápido, mas poderá deixar seus usuários mais felizes com a velocidade que você tem.
fonte
Passo a maior parte da minha vida apenas neste lugar. Os traços gerais são para executar seu criador de perfil e registrá-lo:
__restrict
liberalmente para prometer ao compilador sobre alias.E mais uma coisa que gosto de fazer:
fonte
Examples on the PowerPC ...
<- Ou seja, algumas implementações do PowerPC. O PowerPC é um ISA, não uma CPU.lea
.Jogue mais hardware nele!
fonte
Mais sugestões:
Evitar E / S : qualquer E / S (disco, rede, portas etc.) sempre será muito mais lenta que qualquer código que esteja executando cálculos; portanto, livre-se de qualquer E / S desnecessária.
Mover E / S antecipadamente : carregue todos os dados necessários para um cálculo antecipado, para que você não tenha repetidas esperas de E / S no núcleo de um algoritmo crítico (e talvez como resultado repetido procura de disco, ao carregar todos os dados em uma ocorrência, pode evitar a busca).
Atraso na E / S : não anote seus resultados até o término do cálculo, armazene-os em uma estrutura de dados e despeje-os de uma só vez no final, quando o trabalho duro for concluído.
E / S encadeada : para aqueles que ousam o suficiente, combine 'E / S adiantada' ou 'Atraso E / S' com o cálculo real movendo o carregamento para um encadeamento paralelo, para que enquanto estiver carregando mais dados, você possa trabalhar em um cálculo dos dados que você já possui ou enquanto calcula o próximo lote de dados, você pode escrever simultaneamente os resultados do último lote.
fonte
mmap()
para entrada, façamadvise()
chamadas apropriadas e useaio_write()
para escrever grandes blocos de saída (= alguns MiB).Como muitos dos problemas de desempenho envolvem problemas no banco de dados, darei algumas dicas específicas ao ajustar consultas e procedimentos armazenados.
Evite cursores na maioria dos bancos de dados. Evite fazer loop também. Na maioria das vezes, o acesso a dados deve ser baseado em conjunto, não registrado por processamento de registro. Isso inclui a não reutilização de um único procedimento armazenado de registro quando você deseja inserir 1.000.000 de registros de uma só vez.
Nunca use select *, apenas retorne os campos que você realmente precisa. Isso é especialmente verdadeiro se houver junções, pois os campos de junção serão repetidos e, portanto, causarão uma carga desnecessária no servidor e na rede.
Evite o uso de subconsultas correlacionadas. Use junções (incluindo junções a tabelas derivadas sempre que possível) (eu sei que isso é verdade para o Microsoft SQL Server, mas teste o conselho ao usar um back-end diferente).
Índice, índice, índice. E atualize essas estatísticas, se aplicável ao seu banco de dados.
Torne a consulta sargável . O significado evita coisas que tornam impossível o uso de índices, como o uso de um curinga no primeiro caractere de uma cláusula like ou de uma função na junção ou como a parte esquerda de uma instrução where.
Use tipos de dados corretos. É mais rápido fazer a matemática da data em um campo de data do que tentar converter um tipo de dados da cadeia de caracteres em um tipo de dados da data e, em seguida, fazer o cálculo.
Nunca coloque um loop de qualquer tipo em um gatilho!
A maioria dos bancos de dados tem uma maneira de verificar como a execução da consulta será feita. No Microsoft SQL Server, isso é chamado de plano de execução. Verifique os primeiros para ver onde estão as áreas problemáticas.
Considere com que frequência a consulta é executada e quanto tempo leva para executar ao determinar o que precisa ser otimizado. Às vezes, você pode obter mais desempenho com um pequeno ajuste em uma consulta que é executada milhões de vezes por dia do que com o tempo gasto em uma consulta de execução longa que é executada apenas uma vez por mês.
Use algum tipo de ferramenta de criação de perfil para descobrir o que realmente está sendo enviado para e do banco de dados. Lembro-me de uma vez no passado em que não conseguimos descobrir por que a página era tão lenta para carregar quando o procedimento armazenado era rápido e descobri através de criação de perfil que a página da Web estava solicitando a consulta muitas vezes, em vez de uma vez.
O criador de perfil também ajudará você a descobrir quem está bloqueando quem. Algumas consultas executadas rapidamente enquanto são executadas sozinhas podem ficar muito lentas devido a bloqueios de outras consultas.
fonte
O fator limitante mais importante atualmente é a largura de banda de memória limitada . Os multicores estão apenas piorando isso, pois a largura de banda é compartilhada entre os núcleos. Além disso, a área limitada de chips dedicada à implementação de caches também é dividida entre núcleos e threads, agravando ainda mais esse problema. Finalmente, a sinalização entre chips necessária para manter os diferentes caches coerentes também aumenta com um número maior de núcleos. Isso também adiciona uma penalidade.
Esses são os efeitos que você precisa gerenciar. Às vezes, através do microgerenciamento do seu código, mas, às vezes, pela consideração cuidadosa e refatoração.
Muitos comentários já mencionam o código amigável do cache. Existem pelo menos dois sabores distintos disso:
O primeiro problema especificamente tem a ver com tornar seus padrões de acesso a dados mais regulares, permitindo que o pré-buscador de hardware funcione com eficiência. Evite a alocação dinâmica de memória, que espalha seus objetos de dados na memória. Use recipientes lineares em vez de listas, hashes e árvores vinculadas.
O segundo problema tem a ver com a melhoria da reutilização de dados. Altere seus algoritmos para trabalhar em subconjuntos de dados que se encaixam no cache disponível e reutilize esses dados o máximo possível enquanto ainda estiverem no cache.
Empacotar dados com mais força e garantir que você use todos os dados nas linhas de cache nos hot loops ajudará a evitar esses outros efeitos e permitirá ajustar dados mais úteis no cache.
fonte
fonte
Embora eu goste da resposta de Mike Dunlavey, na verdade é uma ótima resposta com exemplo de apoio, acho que poderia ser expresso de maneira muito simples:
Descubra o que leva mais tempo primeiro e entenda o porquê.
É o processo de identificação dos porcos do tempo que ajuda a entender onde você deve refinar seu algoritmo. Essa é a única resposta independente de linguagem que posso encontrar para um problema que já deveria estar totalmente otimizado. Também presumindo que você queira ser independente da arquitetura em sua busca por velocidade.
Portanto, embora o algoritmo possa ser otimizado, sua implementação pode não ser. A identificação permite que você saiba qual parte é qual: algoritmo ou implementação. Portanto, o que mais dificulta o tempo é o seu principal candidato para a revisão. Mas como você diz que deseja extrair os últimos%, você também pode examinar as partes menores, as que você não examinou tão atentamente a princípio.
Por fim, um pouco de tentativa e erro com números de desempenho de diferentes maneiras de implementar a mesma solução, ou algoritmos potencialmente diferentes, pode trazer informações que ajudam a identificar desperdiçadores de tempo e poupadores de tempo.
HPH, também.
fonte
Você provavelmente deve considerar a "perspectiva do Google", ou seja, determinar como seu aplicativo pode se tornar amplamente paralelo e simultâneo, o que inevitavelmente também significará, em algum momento, a distribuição do seu aplicativo por diferentes máquinas e redes, para que ele possa escalar idealmente quase linearmente com o hardware que você joga nele.
Por outro lado, o pessoal do Google também é conhecido por disponibilizar muita mão de obra e recursos para resolver alguns dos problemas em projetos, ferramentas e infraestrutura que estão usando, como por exemplo , otimização de todo o programa para o gcc , com uma equipe dedicada de engenheiros. invadir internamente o gcc para prepará-lo para cenários de casos de uso típicos do Google.
Da mesma forma, criar um perfil de um aplicativo não significa mais simplesmente criar um perfil do código do programa, mas também de todos os seus sistemas e infraestrutura (redes, comutadores, servidores, matrizes RAID) para identificar redundâncias e potencial de otimização do ponto de vista do sistema.
fonte
fonte
fonte
Dividir e conquistar
Se o conjunto de dados que está sendo processado for muito grande, faça um loop sobre pedaços dele. Se você fez seu código corretamente, a implementação deve ser fácil. Se você tem um programa monolítico, agora sabe melhor.
fonte
Antes de tudo, como mencionado em várias respostas anteriores, aprenda o que afeta seu desempenho - é memória ou processador, rede ou banco de dados ou outra coisa. Dependendo disso ...
... se for memória - encontre um dos livros escritos há muito tempo por Knuth, uma das séries "The Art of Computer Programming". Provavelmente, trata-se de classificação e pesquisa - se minha memória estiver errada, você terá que descobrir em que ele fala sobre como lidar com o armazenamento lento de dados em fita. Transforme mentalmente seu aperto de memória / fita par de em seu par de cache / memória principal (ou em par de cache L1 / L2), respectivamente. Estude todos os truques que ele descreve - se você não encontrar algo que resolva seu problema, contrate um cientista da computação profissional para realizar uma pesquisa profissional. Se seu problema de memória ocorrer por acaso com a FFT (o cache falha em índices com inversão de bits ao fazer borboletas radix-2), não contrate um cientista. Em vez disso, otimize manualmente as passagens uma a uma até que você ' até os últimos por cento, certo? Se forem poucos , você provavelmente vencerá.
... se for processador - mude para a linguagem assembly. Estudo da especificação do processador - o que leva carrapatos , VLIW, SIMD. As chamadas de função são provavelmente devoradoras de carrapatos substituíveis. Aprenda transformações de loop - pipeline, desenrole. Múltiplas e divisões podem ser substituíveis / interpoladas com deslocamento de bits (multiplicações por números inteiros pequenos podem ser substituíveis por adições). Tente truques com dados mais curtos - se você tiver sorte, uma instrução com 64 bits pode ser substituída por duas em 32 ou até 4 em 16 ou 8 em 8 bits. Tente também mais tempodados - por exemplo, seus cálculos de flutuação podem ficar mais lentos que o dobro em um processador específico. Se você tem coisas trigonométricas, lute com tabelas pré-calculadas; Lembre-se também de que o seno de pequeno valor pode ser substituído por esse valor se a perda de precisão estiver dentro dos limites permitidos.
... se for em rede - pense em compactar os dados que você passar por eles. Substitua a transferência XML por binária. Protocolos de estudo. Experimente o UDP em vez do TCP se você conseguir lidar com a perda de dados.
... se for banco de dados, vá a qualquer fórum de banco de dados e peça conselhos. Grade de dados na memória, otimização do plano de consulta etc etc etc.
HTH :)
fonte
Armazenamento em cache! Uma maneira barata (no esforço do programador) de tornar quase tudo mais rápido é adicionar uma camada de abstração de cache a qualquer área de movimentação de dados do seu programa. Seja E / S ou apenas passando / criação de objetos ou estruturas. Muitas vezes, é fácil adicionar caches às classes de fábrica e aos leitores / gravadores.
Às vezes, o cache não ganha muito, mas é um método fácil adicionar apenas o cache e desabilitá-lo onde não ajudar. Eu sempre achei isso para obter um desempenho enorme sem ter que micro-analisar o código.
fonte
Eu acho que isso já foi dito de uma maneira diferente. Porém, ao lidar com um algoritmo intensivo de processador, você deve simplificar tudo dentro do loop mais interno às custas de todo o resto.
Isso pode parecer óbvio para alguns, mas é algo em que tento me concentrar, independentemente do idioma em que estou trabalhando. Se você está lidando com loops aninhados, por exemplo, e encontra uma oportunidade de reduzir algum código em um nível, pode, em alguns casos, acelerar drasticamente seu código. Como outro exemplo, há pequenas coisas em que pensar, como trabalhar com números inteiros em vez de variáveis de ponto flutuante sempre que possível e usar multiplicação em vez de divisão sempre que possível. Novamente, essas são coisas que devem ser consideradas para o seu loop mais interno.
Às vezes, você pode encontrar o benefício de executar suas operações matemáticas em um número inteiro dentro do loop interno e reduzi-lo a uma variável de ponto flutuante com a qual você pode trabalhar posteriormente. Esse é um exemplo de sacrifício de velocidade em uma seção para melhorar a velocidade em outra, mas em alguns casos o pagamento pode valer a pena.
fonte
Passei algum tempo trabalhando na otimização de sistemas de negócios cliente / servidor operando em redes de baixa largura de banda e longa latência (por exemplo, satélite, remoto, offshore) e consegui algumas melhorias dramáticas no desempenho com um processo bastante repetitivo.
Medida : Comece entendendo a capacidade e a topologia subjacentes da rede. Conversando com as pessoas relevantes da rede nos negócios e utilize ferramentas básicas, como ping e traceroute, para estabelecer (no mínimo) a latência da rede de cada local do cliente, durante períodos operacionais típicos. Em seguida, faça medições precisas de tempo de funções específicas do usuário final que exibem os sintomas problemáticos. Registre todas essas medidas, juntamente com seus locais, datas e horas. Considere criar a funcionalidade de "teste de desempenho de rede" do usuário final em seu aplicativo cliente, permitindo que seus usuários avançados participem do processo de melhoria; capacitá-los dessa maneira pode ter um enorme impacto psicológico quando você lida com usuários frustrados por um sistema com desempenho insatisfatório.
Analisar : Usando todo e qualquer método de registro disponível para estabelecer exatamente quais dados estão sendo transmitidos e recebidos durante a execução das operações afetadas. Idealmente, seu aplicativo pode capturar dados transmitidos e recebidos pelo cliente e pelo servidor. Se estes incluem também carimbos de data e hora, melhor ainda. Se o registro suficiente não estiver disponível (por exemplo, sistema fechado ou incapacidade de implantar modificações em um ambiente de produção), use um sniffer de rede e verifique se realmente entende o que está acontecendo no nível da rede.
Cache : procure casos em que dados estáticos ou alterados com pouca frequência estejam sendo transmitidos repetidamente e considere uma estratégia de cache apropriada. Exemplos típicos incluem valores de "lista de seleção" ou outras "entidades de referência", que podem ser surpreendentemente grandes em alguns aplicativos de negócios. Em muitos casos, os usuários podem aceitar que eles devem reiniciar ou atualizar o aplicativo para atualizar dados atualizados com pouca frequência, especialmente se ele puder economizar um tempo significativo da exibição dos elementos da interface do usuário comumente usados. Certifique-se de entender o comportamento real dos elementos de armazenamento em cache já implementados - muitos métodos comuns de armazenamento em cache (por exemplo, HTTP ETag) ainda exigem uma viagem de ida e volta à rede para garantir consistência e, onde a latência da rede é cara, você pode evitá-lo completamente com uma abordagem de armazenamento em cache diferente.
Paralelamente : procure transações seqüenciais que não precisam ser emitidas logicamente estritamente em sequência e refaça o trabalho do sistema para emiti-las em paralelo. Lidei com um caso em que uma solicitação de ponta a ponta tinha um atraso de rede inerente de ~ 2s, o que não era um problema para uma única transação, mas quando eram necessárias 6 viagens de ida e volta sequenciais 2s antes que o usuário recuperasse o controle do aplicativo cliente. , tornou-se uma enorme fonte de frustração. Descobrir que essas transações eram de fato independentes permitiu que fossem executadas em paralelo, reduzindo o atraso do usuário final para muito próximo ao custo de uma única viagem de ida e volta.
Combinar : onde as solicitações sequenciais devem ser executadas sequencialmente, procure oportunidades para combiná-las em uma única solicitação mais abrangente. Exemplos típicos incluem a criação de novas entidades, seguidos de solicitações para relacionar essas entidades a outras entidades existentes.
Compactar : procure oportunidades para alavancar a compactação da carga útil, substituindo um formulário textual por um binário ou usando a tecnologia de compactação real. Muitas pilhas modernas de tecnologia (isto é, dentro de uma década) suportam isso quase de forma transparente, portanto, verifique se está configurado. Muitas vezes, fico surpreso com o impacto significativo da compactação, onde parecia claro que o problema era fundamentalmente latência e não largura de banda, descobrindo depois que permitia que a transação cabesse em um único pacote ou evitava a perda de pacotes e, portanto, tinha um tamanho excessivo. impacto no desempenho.
Repita : volte ao início e meça novamente suas operações (nos mesmos locais e horários) com as melhorias implementadas, registre e relate seus resultados. Como em toda otimização, alguns problemas podem ter sido resolvidos expondo outros que agora dominam.
Nas etapas acima, concentro-me no processo de otimização relacionado ao aplicativo, mas é claro que você deve garantir que a própria rede subjacente esteja configurada da maneira mais eficiente para oferecer suporte ao seu aplicativo também. Envolva os especialistas em rede nos negócios e determine se eles podem aplicar melhorias de capacidade, QoS, compactação de rede ou outras técnicas para resolver o problema. Geralmente, eles não entendem as necessidades de seu aplicativo, por isso é importante que você esteja preparado (após a etapa Analisar) para discuti-lo com eles e também faça um argumento comercial quanto a quaisquer custos que você solicitará . Encontrei casos em que a configuração de rede incorreta fazia com que os dados dos aplicativos fossem transmitidos por um link lento por satélite, e não por um link terrestre, simplesmente porque estava usando uma porta TCP que não era "bem conhecida" pelos especialistas em redes; obviamente, a correção de um problema como esse pode ter um impacto dramático no desempenho, sem a necessidade de alterações no código ou na configuração do software.
fonte
Muito difícil dar uma resposta genérica para esta pergunta. Realmente depende do domínio do problema e da implementação técnica. Uma técnica geral que é bastante neutra em termos de idioma: identifique pontos de acesso de código que não podem ser eliminados e otimize manualmente o código do assembler.
fonte
Os últimos% são uma coisa muito dependente da CPU e do aplicativo ....
A lista continua .... Mas esse tipo de coisa realmente é o último recurso ...
Crie para x86 e execute Valgrind / Cachegrind no código para criar um perfil de desempenho adequado. Ou o CCStudio da Texas Instruments tem um bom perfil. Então você realmente saberá onde se concentrar ...
fonte
Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?
Para qualquer projeto não offline, apesar de ter o melhor software e o melhor hardware, se sua taxa de transferência for fraca, essa linha fina irá espremer dados e causar atrasos, embora em milissegundos ... mas se você estiver falando das últimas quedas , são algumas gotas obtidas, 24 horas por dia, 7 dias por semana para qualquer pacote enviado ou recebido.
fonte
Não é tão profundo ou complexo quanto as respostas anteriores, mas aqui vai: (estas são mais para iniciantes / intermediários)
fonte
Impossível dizer. Depende da aparência do código. Se pudermos assumir que o código já existe, podemos simplesmente olhar para ele e descobrir a partir disso, como otimizá-lo.
Melhor localidade do cache, desenrolamento de loop, Tente eliminar longas cadeias de dependência, para obter um melhor paralelismo no nível das instruções. Prefira movimentos condicionais sobre ramificações quando possível. Explore as instruções do SIMD quando possível.
Entenda o que seu código está fazendo e o hardware em que está sendo executado. Torna-se bastante simples determinar o que você precisa fazer para melhorar o desempenho do seu código. Esse é realmente o único conselho verdadeiramente geral em que consigo pensar.
Bem, isso e "Mostre o código no SO e peça conselhos de otimização para esse trecho de código específico".
fonte
Se um hardware melhor é uma opção, definitivamente faça isso. De outra forma
fonte
O caminho do Google é uma opção "Coloque em cache. Sempre que possível, não toque no disco"
fonte
Aqui estão algumas técnicas de otimização rápidas e sujas que eu uso. Considero isso uma otimização de 'primeira passagem'.
Saiba onde é gasto o tempo Descubra exatamente o que está demorando. É arquivo IO? É hora da CPU? É a rede? É o banco de dados? É inútil otimizar para E / S se esse não for o gargalo.
Conheça o seu ambiente Saber onde otimizar normalmente depende do ambiente de desenvolvimento. No VB6, por exemplo, passar por referência é mais lento que passar por valor, mas em C e C ++, por referência é muito mais rápido. Em C, é razoável tentar algo e fazer algo diferente se um código de retorno indicar uma falha, enquanto no Dot Net, as exceções de captura são muito mais lentas do que procurar uma condição válida antes de tentar.
Índices Crie índices nos campos do banco de dados consultados com freqüência. Você quase sempre pode trocar espaço por velocidade.
Evitar pesquisas Dentro do loop para ser otimizado, evito ter que fazer pesquisas. Encontre o deslocamento e / ou índice fora do loop e reutilize os dados dentro.
Minimize as E / S tente projetar de uma maneira que reduz o número de vezes que você precisa ler ou gravar, especialmente em uma conexão em rede
Reduzir abstrações Quanto mais camadas de abstração o código tiver que trabalhar, mais lento será. Dentro do loop crítico, reduza abstrações (por exemplo, revele métodos de nível inferior que evitam código extra)
Gerar Threads para projetos com uma interface de usuário, gerando um novo thread para realizar tarefas mais lentas faz com que o aplicativo pareça mais responsivo, embora não seja.
Pré-processo Geralmente, você pode trocar espaço por velocidade. Se houver cálculos ou outras operações intensas, verifique se você pode pré-calcular algumas informações antes de entrar no ciclo crítico.
fonte
Se você tiver um monte de matemática de ponto flutuante altamente paralela, especialmente com precisão única, tente descarregá-la para um processador gráfico (se houver) usando OpenCL ou (para chips NVidia) CUDA. As GPUs têm imenso poder de computação de ponto flutuante em seus shaders, o que é muito maior que o de uma CPU.
fonte
Adicionando esta resposta, já que não a vi incluída em todas as outras.
Minimize a conversão implícita entre tipos e sinal:
Isso se aplica ao C / C ++, pelo menos, mesmo se você já pensa que está livre de conversões - às vezes é bom testar a adição de avisos do compilador em torno de funções que exigem desempenho, principalmente atenção a conversões em loops.
Específico do GCC: você pode testar isso adicionando alguns pragmas detalhados ao seu código,
Vi casos em que você pode obter uma aceleração de alguns por cento, reduzindo as conversões geradas por avisos como este.
Em alguns casos, tenho um cabeçalho com avisos estritos que mantenho incluídos para evitar conversões acidentais; no entanto, isso é uma troca, já que você pode adicionar muitos elencos para conversões intencionais silenciosas, o que pode tornar o código mais confuso por um mínimo de tempo. ganhos.
fonte
Às vezes, alterar o layout dos seus dados pode ajudar. Em C, você pode alternar de uma matriz ou estruturas para uma estrutura de matrizes ou vice-versa.
fonte
Ajuste o sistema operacional e a estrutura.
Pode parecer um exagero, mas pense assim: Sistemas operacionais e estruturas são projetados para fazer muitas coisas. Seu aplicativo faz apenas coisas muito específicas. Se você conseguir fazer com que o sistema operacional faça exatamente o que seu aplicativo precisa e faça com que ele entenda como a estrutura (php, .net, java) funciona, você poderá melhorar muito seu hardware.
O Facebook, por exemplo, mudou algumas coisas no nível do kernel no Linux, mudou o funcionamento do memcached (por exemplo, eles escreveram um proxy memcached e usaram o udp em vez do tcp ).
Outro exemplo para isso é o Window2008. O Win2K8 possui uma versão em que você pode instalar apenas o sistema operacional básico necessário para executar aplicativos X (por exemplo, aplicativos da Web, aplicativos de servidor). Isso reduz grande parte da sobrecarga que o sistema operacional possui nos processos em execução e oferece melhor desempenho.
Claro, você sempre deve usar mais hardware como o primeiro passo ...
fonte