As práticas recomendadas de otimização
Este documento descreve algumas das práticas recomendadas para a otimização do Visual C++. Os seguintes tópicos são abordados:
Compilador e vinculador opções
Otimização Guiada por perfil
Que nível de otimização deve usar?
Os Switches de ponto de flutuante
Otimização Declspecs
Pragmas de otimização
__restrict e __assume
Suporte intrínseco
Exceptions
Compilador e vinculador opções
Otimização Guiada por perfil
Visual C++ oferece suporte à Otimização Guiada por perfil (PGO). Essa otimização usa dados de perfil de execuções anteriores de uma versão instrumentada de um aplicativo para orientar a otimização mais recente do aplicativo. Usar o PGO pode ser demorado, então não pode ser algo que todo desenvolvedor usa, mas recomendamos o uso de PGO para a compilação de versão final de um produto. For more information, see Otimizações guiadas por perfil.
Além disso, a otimização de programa inteiro (também conhecidas como geração de código de tempo de Link) e o /O1 e /O2 otimizações foram aprimorados. Em geral, um aplicativo compilado com uma dessas opções será mais rápido do que o mesmo aplicativo compilado com um compilador anterior.
For more information, see /GL (otimização de programa total) and / O1, /O2. (tamanho de minimizar, maximizar velocidade).
Que nível de otimização deve usar?
Se possível, a versão final compilações devem ser compiladas com otimizações guiadas por perfil. Se não for possível construir com a PGO, seja devido à infra-estrutura insuficiente para executar compilações instrumentadas ou não ter acesso aos cenários, sugerimos a construção com otimização de programa total.
O /Gy também é muito útil. Ele gera um COMDAT separado para cada função, dando o vinculador mais flexibilidade quando se trata de remoção não referenciados COMDATs e COMDAT dobra. A única desvantagem de usar /Gy é que ele pode ter um efeito secundário no tempo de compilação. Portanto, geralmente, é recomendável usá-lo. For more information, see /GY (nível de função para ativar a vinculação).
Para vincular-se em ambientes de 64 bits, é recomendável usar o /OPT:REF,ICF a opção de vinculador e em ambientes de 32 bits, /OPT:REF é recomendado. For more information, see / OPT (otimizações).
É também recomendável para gerar os símbolos de depuração, mesmo com compilações de versão otimizada. Ele não afeta o código gerado e torna muito mais fácil de depurar seu aplicativo, se precisa ser.
Os Switches de ponto de flutuante
O /Op opção de compilador foi removida e as quatro opções do compilador lidando com flutuante ponto otimizações foram adicionadas:
/fp:precise |
Isso é a recomendação de padrão e deve ser usado na maioria dos casos. |
/fp:fast |
Recomendado se o desempenho é mais importante, por exemplo, em jogos. Isso fará com que o desempenho mais rápido. |
/fp:strict |
Recomendado se exceções de ponto flutuante de precisa e IEEE comportamento é desejado. Isso fará com que o desempenho mais lento. |
/fp:except[-] |
Pode ser usado em conjunto com /fp:strict ou /fp:precise, mas não /fp:fast. |
For more information, see /FP (Especifica o comportamento de ponto flutuante).
Otimização Declspecs
Nesta seção, examinaremos duas declspecs que pode ser usados em programas para ajudar o desempenho: __declspec(restrict) and __declspec(noalias).
O restrict declspec só pode ser aplicado a declarações de função que retornam um ponteiro, como __declspec(restrict) void *malloc(size_t size);
O restrict declspec é usada em funções que retornam ponteiros de unaliased. Esta palavra-chave é usada para a implementação do C Runtime Library de malloc , pois nunca retornará um valor de ponteiro que já está em uso no programa atual (a menos que você está fazendo algo ilegal, como, por exemplo, usando a memória após ela ter sido liberada).
O restrict declspec oferece o compilador de mais informações para realizar otimizações do compilador. Uma das coisas mais difíceis de um compilador determinar é o alias de ponteiros outros ponteiros e usando essas informações bastante ajuda o compilador.
É pena salientar que esta é uma promessa para o compilador não algo que o compilador irá verificar. Se o seu programa usa esse restrict declspec inadequadamente, seu programa poderá ter comportamento incorreto.
For more information, see restrict.
O noalias declspec também é aplicada somente às funções e indica que a função é uma função semi-pure. Uma função semi-pure é aquele que faz referência ou modifica somente locals, argumentos e indirections de primeiro nível de argumentos. Este declspec é uma promessa ao compilador e se a função referencia globals ou indirections de segundo nível de argumentos de ponteiro, em seguida, o compilador podem gerar código que interrompe o aplicativo.
For more information, see noalias.
Pragmas de otimização
Há também vários pragmas úteis para ajudar a otimizar o código. A primeira, discutiremos é #pragma optimize:
#pragma optimize("{opt-list}", on | off)
Esse pragma lhe permite definir um nível de otimização de determinada função por função. Isso é ideal para esses raras ocasiões onde seu aplicativo travar quando uma determinada função é compilada com a otimização. Você pode usar isso para desativar otimizações para uma única função:
#pragma optimize("", off)
int myFunc() {...}
#pragma optimize("", on)
For more information, see optimize.
Inlining é uma das otimizações mais importantes que o compilador executa e aqui podemos falar sobre alguns pragmas de ajudá-lo a modificar esse comportamento.
#pragma inline_recursioné útil para especificar se deseja ou não o aplicativo consiga in-line uma chamada recursiva. Por padrão, ele está desativado. Para a recursão superficial das funções de pequenas, você pode para ativar isso. For more information, see inline_recursion.
Outro pragma útil para limitar a profundidade de inlining é #pragma inline_depth. Isso geralmente é útil em situações onde você está tentando limitar o tamanho de um programa ou a função. For more information, see inline_depth.
__restrict e __assume
Há algumas palavras-chave no Visual C++ que podem ajudar o desempenho: __restrict and __assume.
Primeiro, deve-se observar que __restrict e __declspec(restrict) são duas coisas diferentes. Embora um pouco estão relacionados, sua semântica é diferente. __restricté um qualificador de tipo, como const ou volatile, mas exclusivamente para tipos de ponteiro.
Um ponteiro que seja modificado com __restrict é conhecido como um __restrict o ponteiro. Um ponteiro de __restrict é um ponteiro que só pode ser acessado através do ponteiro de __restrict. Em outras palavras, o ponteiro de outro não pode ser usado para acessar os dados apontados pelo ponteiro __restrict.
__restrictpode ser uma poderosa ferramenta para o otimizador do Visual C++, mas usá-la com muito cuidado. Se usada incorretamente, o otimizador pode realizar uma otimização que violaria a seu aplicativo.
O __restrict substitui a palavra-chave a /Oa Alternar de versões anteriores.
Com __assume, um desenvolvedor pode dizer ao compilador fazer suposições sobre o valor da variável alguns.
Por exemplo __assume(a < 5); informa o otimizador que nessa linha de código a variável a for menor que 5. Novamente, essa é uma promessa do compilador. Se a é realmente 6 nesse ponto no programa e o comportamento do programa após otimizou o compilador pode não ser o que você esperaria. __assumeé mais útil antes para instruções de switch e/ou expressões condicionais.
Existem algumas limitações para __assume. Primeiro, como __restrict, é apenas uma sugestão, portanto, o compilador está livre para ignorar o proprietário. Além disso, __assume atualmente só funciona com as desigualdades variáveis contra constantes. Não se propaga inequações simbólicas, por exemplo, assume(a < b).
Suporte intrínseco
Intrínsecos são função chama onde o compilador não tem conhecimento intrínseco sobre a chamada e, em vez de chamar uma função em uma biblioteca, ele emite código para essa função. O intrin.h do arquivo de cabeçalho localizado em <Installation_Directory> \VC\include\intrin.h contém todos os os intrínsecos disponíveis para cada um dos três plataformas (x x86, x64 e Itanium).
Intrínsecos possibilitam o programador ir profundamente o código sem ter que usar o assembly. Existem diversas vantagens na utilização intrínsecos:
Seu código é mais portátil. Vários os intrínsecos estão disponíveis em várias arquiteturas de CPU.
Seu código é mais fácil de ler, desde que o código ainda está escrito em C/C++.
Seu código obtém o benefício das otimizações do compilador. Como o compilador fica melhor, melhora a geração de código para os intrínsecos.
For more information, see Compiler Intrinsics and Benefits of Using Intrinsics.
Exceptions
Há um desempenho hit associado ao uso de exceções. Algumas restrições são apresentadas ao usar blocos try inibem o compilador de executar determinadas otimizações. No x86 plataformas que não há comprometimento do desempenho adicionais tente blocos devido às informações de estado adicional que deve ser gerada durante a execução de código. Em plataformas de 64 bits, tente blocos não prejudicar o desempenho máximo, mas depois que uma exceção é lançada, o processo de localizar o manipulador e desenrolar a pilha pode ser caro.
Portanto, é recomendável para evitar a introdução de blocos try/catch no código que realmente desnecessário. Se você precisar usar exceções, use exceções síncronas, se possível. For more information, see Structured Exception Handling (C++).
Por fim, lança exceções para somente a casos excepcionais. Usando exceções para o fluxo de controle geral provavelmente fará desempenho sofrer.