Quando usar a função embutida e quando não usá-la?

185

Eu sei que inline é uma dica ou solicitação para o compilador e é usada para evitar despesas gerais de chamada de função.

Então, com que base se pode determinar se uma função é candidata a inclusão ou não? Nesse caso, deve-se evitar inline?

Ashish
fonte
11
inlineé o C ++ recém-chegado que CFLAGSsão para o recém-chegado Gentoo: não, compilando com -O3 -funroll-loops -finline-functionsnão vai fazer o seu velho Pentium voar;)
Gregory Pakosz
1
Uma razão para não usar inline é que alguns depuradores não permitem definir um ponto de interrupção ou entrar em uma função embutida.
23410 Rob deFriesse
1
Duplicar: stackoverflow.com/questions/1875947/...
Steve Jessop
5
Você não deve determinar se uma função deve ser incorporada ou não. Deixe o compilador fazer isso; é melhor do que você (e pode integrar funções seletivamente com base no ambiente de cada chamada).
22815 David Thornley
@DavidThornley Às vezes, mesmo com o sinalizador O3 definido, o compilador não alinha a função se a definição estiver no arquivo cpp. Portanto, a regra geral que sigo é alinhar os liners e também as funções sem loops.
talekeDskobeDa

Respostas:

210

Evitar o custo de uma chamada de função é apenas metade da história.

Faz:

  • use em inlinevez de#define
  • funções muito pequenas são boas candidatas a inline: código mais rápido e executáveis ​​menores (mais chances de permanecer no cache de código)
  • a função é pequena e chamada com muita frequência

não:

  • funções grandes: leva a executáveis ​​maiores, o que prejudica significativamente o desempenho, independentemente da execução mais rápida resultante da sobrecarga de chamada
  • funções embutidas vinculadas à E / S
  • a função raramente é usada
  • construtores e destruidores: mesmo quando vazio, o compilador gera código para eles
  • quebrando a compatibilidade binária ao desenvolver bibliotecas:
    • embutir uma função existente
    • alterar uma função embutida ou tornar uma função embutida não embutida: a versão anterior da biblioteca chama a implementação antiga

ao desenvolver uma biblioteca, para tornar uma classe extensível no futuro, você deve:

  • adicione destruidor virtual não interno, mesmo que o corpo esteja vazio
  • tornar todos os construtores não-inline
  • gravar implementações não em linha do construtor de cópia e operador de atribuição, a menos que a classe não possa ser copiada por valor

Lembre-se de que a inlinepalavra-chave é uma dica para o compilador: o compilador pode decidir não incorporar uma função e pode incorporar funções que não foram marcadas inlineem primeiro lugar. Eu geralmente evito marcar a funçãoinline (além, talvez, ao escrever funções muito pequenas).

Sobre o desempenho, a abordagem inteligente é (como sempre) criar um perfil do aplicativo e, eventualmente, inlineum conjunto de funções que representam um gargalo.

Referências:


Edição: Bjarne Stroustrup, a linguagem de programação C ++:

A função pode ser definida para ser inline. Por exemplo:

inline int fac(int n)
{
  return (n < 2) ? 1 : n * fac(n-1);
}

O inlineespecificador é uma dica para o compilador de que ele deve tentar gerar código para uma chamada em fac()linha em vez de definir o código para a função uma vez e depois chamar pelo mecanismo de chamada de função usual. Um compilador inteligente pode gerar a constante 720para uma chamada fac(6). A possibilidade de funções em linha recursivas mutuamente, funções em linha que se repetem ou não, dependendo da entrada, etc., torna impossível garantir que todas as chamadas de uma inlinefunção sejam realmente embutidas. O grau de inteligência de um compilador não pode ser legislado; portanto, um compilador pode gerar 720, outro 6 * fac(5)e, ainda assim, uma chamada não-alinhadafac(6) .

Para tornar possível o alinhamento na ausência de recursos incomuns de compilação e vinculação, a definição - e não apenas a declaração - de uma função embutida deve estar no escopo (§9.2). Um inlineespecificador não afeta a semântica de uma função. Em particular, uma função embutida ainda possui um endereço exclusivo e também staticvariáveis ​​(§7.1.2) de uma função embutida.

EDIT2: ISO-IEC 14882-1998, 7.1.2 Especificadores de função

Uma declaração de função (8.3.5, 9.3, 11.4) com um inlineespecificador declara uma função embutida. O especificador em linha indica para a implementação que a substituição em linha do corpo da função no ponto de chamada deve ser preferida ao mecanismo usual de chamada de função. Não é necessária uma implementação para executar essa substituição embutida no ponto de chamada; no entanto, mesmo que essa substituição em linha seja omitida, as outras regras para funções em linha definidas em 7.1.2 ainda serão respeitadas.

Gregory Pakosz
fonte
34
inlineé muito mais do que uma dica para o compilador. Ele altera as regras de linguagem sobre várias definições. Além disso, ter dados estáticos não é um motivo de ferro fundido para evitar a inclusão de uma função. A implementação é obrigada a alocar um único objeto estático para cada função estática, independentemente de a função ser declarada inlineou não. As classes ainda são extensíveis se tiverem construtores embutidos e destruidores virtuais. E o destruidor de chaves vazias é a única função virtual que às vezes é uma boa ideia deixar em linha.
22310 CB Bailey
2
É uma dica no sentido de que a função não acaba necessariamente embutida (mas inglês não é minha língua materna). Sobre a estática nas funções marcadas inline, o resultado é que a função não é incorporada: você paga o preço pela chamada e também cada unidade de tradução que inclui e chama a função recebe sua própria cópia do código e das variáveis ​​estáticas. A razão para não inlining construtores e destruidores ao desenvolver uma biblioteca é a compatibilidade binária com versões futuras da sua biblioteca
Gregory Pakosz
14
É impreciso chamá-lo de "dica para o compilador". Na realidade, não- inlinefunções podem ser incorporadas se o compilador quiser. E as inlinefunções não serão incorporadas se o compilador decidir não integrá-las. Como Charles Bailey disse, ele muda as regras de linguagem. Em vez de pensar nisso como uma dica de otimização, é mais preciso pensar nele como um conceito completamente diferente. A inlinepalavra-chave diz ao compilador para permitir várias definições e nada mais. A otimização "inlining" pode ser aplicada a praticamente qualquer função, esteja ela marcada ou não inline.
jalf
26
É que, quando Stroustrup escreve "o especificador em linha é uma dica para o compilador", fico surpreso por ser culpado por citá-lo. De qualquer forma, passei tempo suficiente fazendo o possível para apoiar esta resposta com o máximo de referências possível #
Gregory Pakosz
2
@ GregoryPakosz: Mas nem todos usamos inlinepara obter a função embutida. Às vezes, queremos outros benefícios, como contornar o ODR.
Lightness Races in Orbit -
57

inlinetem muito pouco a ver com otimização. inlineé uma instrução para o compilador para não produzir um erro se a função determinada definição ocorrer várias vezes no programa e uma promessa de que a definição ocorrerá em toda tradução usada e em todos os lugares em que aparecer, ela terá exatamente a mesma definição.

Dadas as regras acima, inlineé adequado para funções curtas cujo corpo não exige a inclusão de dependências extras sobre o que apenas uma declaração precisaria. Toda vez que a definição é encontrada, ela deve ser analisada e o código para seu corpo pode ser gerado, de modo que implica alguma sobrecarga do compilador sobre uma função definida apenas uma vez em um único arquivo de origem.

Um compilador pode incorporar (por exemplo, substituir uma chamada para a função pelo código que executa essa ação dessa função) qualquer chamada de função que ele escolher. Costumava ser "obviamente" inline uma função que não foi declarada na mesma unidade de tradução que a chamada, mas com o uso crescente da otimização do tempo do link, isso ainda não é verdade agora. Igualmente verdadeiro é o fato de que as funções marcadas inlinepodem não estar alinhadas.

CB Bailey
fonte
Tenho a sensação de que isso é mais uma feliz coincidência do que um recurso intencional do C ++. A idéia é muito semelhante às variáveis ​​globais 'estáticas' de C. É uma resposta muito interessante. Eu gostaria que eles tivessem usado apenas uma palavra-chave como 'interna' para indicar ligação interna.
Rehno Lindeque
+1. @ Rehno: Não tenho muita certeza do que você está dizendo. O que a ligação tem a ver com a inlinepalavra - chave? E o que é uma feliz coincidência?
jalf
@ jalf: Lendo meu comentário em retrospecto, percebo que é bastante vago e não tão bem pensado. Definir a mesma função em vários arquivos resulta em um erro de vinculador que pode ser combatido declarando a função 'estática'. No entanto, 'inline' permite que você faça a mesma coisa com diferenças sutis que elas realmente não recebem ligação interna como 'estático'. Suspeito que isso seja realmente mais uma coincidência, porque os implementadores / designers de linguagem perceberam que precisariam fazer algo especial com as funções declaradas nos arquivos de cabeçalho e que foram transferidas para 'inline'.
Rehno Lindeque
4
Não sei por que o seu comentário recebeu tantos votos positivos, já que o desempenho é a principal razão para usar inline.
precisa saber é o seguinte
10

Dizer ao compilador para incorporar uma função é uma otimização, e a regra mais importante da otimização é que a otimização prematura é a raiz de todo mal. Sempre escreva um código claro (usando algoritmos eficientes), depois crie um perfil do seu programa e otimize apenas as funções que estão demorando muito.

Se você achar que uma função específica é muito curta e simples, e está sendo chamada dezenas de milhares de vezes em um loop interno apertado, pode ser um bom candidato.

No entanto, você pode se surpreender - muitos compiladores C ++ incorporam automaticamente pequenas funções para você - e também podem ignorar sua solicitação de inclusão.

dmazzoni
fonte
Na verdade, eu tenho minhas suspeitas de que certos compiladores ignoram completamente 'inline' completamente e respondem apenas a '__inline' ou '__force_inline'. Suponho que isso seja para impedir o abuso!
Rehno Lindeque
Normalmente não é o caso. inline é apenas uma dica, mas é uma dica que a maioria dos compiladores leva a sério. Você pode definir o compilador para emitir a linguagem assembly junto com o código do objeto ( /FAcsno Visual Studio, -sno GCC) para ver exatamente o que ele faz. Na minha experiência, os dois compiladores pesam bastante a palavra-chave inline.
Crashworks
1
É interessante, porque na minha experiência nem o g ++ nem o VC pesam a inlinepalavra-chave. Ou seja, se você inlinevir a função embutida e remover o especificador, ela ainda ficará embutida. Se você tiver exemplos específicos do contrário, compartilhe-os!
Pavel Minaev 19/12/2009
4
como a inlinepalavra - chave impede "limpar código"? A palavra-chave em "otimização prematura" é prematura , não otimização. Dizer que você deve * ativamente evitar otimizações é apenas lixo. O ponto dessa citação é que você deve evitar as otimizações que podem não ser necessárias e ter efeitos colaterais prejudiciais no código (como torná-lo menos sustentável). Não consigo ver como a inlinepalavra-chave tornará o código menos sustentável ou como pode ser prejudicial adicioná-lo a uma função.
jalf
3
No entanto, às vezes, incluir uma função tornará seu código mais lento, não mais rápido. Um exemplo é quando a função é chamada de vários lugares diferentes no seu código; se a função não estiver embutida, ela ainda poderá estar no cache de instruções quando for chamada de um local diferente, e o preditor de ramificação já poderá estar aquecido. Existem alguns padrões que sempre melhoram a eficiência, portanto nunca é demais usá-los. Inlining não é um deles. Geralmente não afeta o desempenho, às vezes ajuda e às vezes dói. Eu mantenho o meu conselho: primeiro perfil, depois inline.
dmazzoni 21/12/2009
5

A melhor maneira de descobrir é criar um perfil do seu programa e marcar pequenas funções que são chamadas muitas vezes e queimar os ciclos da CPU assim inline. A palavra-chave aqui é "pequena" - uma vez que a sobrecarga da chamada da função é insignificante em comparação com o tempo gasto na função, não faz sentido incorporá-las.

O outro uso que eu sugiro é que, se você tem pequenas funções chamadas no código crítico de desempenho com freqüência suficiente para fazer com que um erro de cache seja relevante, provavelmente você também deve incorporá-las. Novamente, é algo que o criador de perfil deve ser capaz de dizer.

Timo Geusch
fonte
4

Otimização prematura é a raiz de todo o mal!

Como regra geral, costumo incluir apenas "getters" e "setters". Quando o código está funcionando e é estável, a criação de perfil pode mostrar quais funções podem se beneficiar com o inlining.

Por outro lado, a maioria dos compiladores modernos possui algoritmos de otimização muito bons e alinham o que você deveria ter indicado.

Reasuming - escreva funções de linha única em linha e se preocupe com outras posteriormente.

Kornel Kisielewicz
fonte
2

As funções embutidas podem melhorar o desempenho do seu código, eliminando a necessidade de inserir argumentos na pilha. se a função em questão estiver em uma parte crítica do seu código, você deverá tomar a decisão inline e não inline na parte de otimização do seu projeto,

você pode ler mais sobre linhas inline no FAQ do c ++

Alon
fonte
1

Costumo usar funções embutidas não como uma otimização, mas para tornar o código mais legível. Às vezes, o código em si é mais curto e fácil de entender do que comentários, nomes descritivos etc. Por exemplo:

void IncreaseCount() { freeInstancesCnt++; }

O leitor conhece imediatamente a semântica completa do código.

danatel
fonte
0

Geralmente sigo uma regra geral em que faço uma função com 3-4 instruções simples como inline. Mas é bom lembrar que é apenas uma dica para o compilador. A chamada final para torná-lo em linha ou não é tomada apenas pelo compilador. Se houver mais do que essas muitas instruções, não declararei em linha, pois com um compilador estúpido, isso pode levar ao inchaço do código.

Naveen
fonte
0

A melhor maneira seria examinar e comparar as instruções geradas para inline e não inline. No entanto, é sempre seguro omitir inline. Usar inlinepode levar a problemas que você não deseja.

Wallyk
fonte
0

Ao decidir sobre o uso em linha, geralmente lembro a seguinte idéia: Nas máquinas modernas, a latência de memória pode ser um gargalo maior que os cálculos brutos. Sabe-se que funções embutidas chamadas frequentemente aumentam o tamanho do executável. Além disso, essa função pode ser armazenada no cache de código da CPU, o que diminuirá o número de falhas de cache quando esse código precisar ser acessado.

Portanto, você deve decidir por si mesmo: inlining aumenta ou diminui o tamanho do código de máquina gerado? Qual é a probabilidade de que chamar a função cause um erro de cache? Se for apimentado em todo o código, eu diria que a probabilidade é alta. Se estiver restrito a um único loop apertado, é provável que a probabilidade seja baixa.

Normalmente, uso inlining nos casos que listo abaixo. No entanto, quando você está realmente preocupado com o desempenho, a criação de perfil é essencial. Além disso, convém verificar se o compilador realmente entende a dica.

  • Rotinas curtas que são chamadas em um loop restrito.
  • Acessores muito básicos (get / set) e funções de wrapper.
  • Infelizmente, o código do modelo nos arquivos de cabeçalho obtém automaticamente a dica embutida.
  • Código de acesso que é usado como uma macro. (Por exemplo, min () / max ())
  • Rotinas matemáticas curtas.
Rehno Lindeque
fonte
0

Além disso, um método embutido tem efeitos colaterais graves ao manter grandes projetos. Quando o código embutido é alterado, todos os arquivos que o utilizam serão reconstruídos automaticamente pelo compilador (é um bom compilador). Isso pode desperdiçar muito do seu tempo de desenvolvimento.

Quando um inlinemétodo é transferido para um arquivo de origem e não é mais incorporado, todo o projeto deve ser reconstruído (pelo menos essa foi a minha experiência). E também quando os métodos são convertidos para embutidos.

Thomas Matthews
fonte
1
Essa é uma questão diferente. Você obtém o problema de reconstrução do código colocado em um arquivo de cabeçalho. Se ele é marcado inlineou não, não importa (excepto sem a inlinepalavra-chave, você poderá obter erros vinculador - mas a inlinechave não é o problema fazendo com que reconstruções excessivos.
jalf
No entanto, alterar um método embutido causará compilações excessivas versus alterar um método não embutido em um arquivo de origem.
21339 Thomas Matthews
0

Deve-se usar o qualificador de função em linha somente quando o código da função for pequeno. Se as funções forem maiores, você deverá preferir as funções normais, pois a economia de espaço na memória vale o sacrifício comparativamente pequeno da velocidade de execução.

agudo
fonte
0

Quando você acha que seu código é pequeno o suficiente para ser usado como in-line e lembre-se da função in-line, duplique-o e cole-o onde a função é chamada, portanto pode ser bom o suficiente para aumentar o tempo de execução, mas também o consumo de memória. Você não pode usar a função embutida quando estiver usando uma função loop / variável estática / recursiva / switch / goto / Virtual. Virtual significa esperar até o tempo de execução e inline significar durante a compilação para que eles não possam ser usados ​​simultaneamente.

sachin pathak
fonte
-2

Eu li algumas respostas e vejo que faltam algumas coisas.

A regra que eu uso não é usar inline, a menos que eu queira que seja inline. Parece bobagem, agora explicação.

Os compiladores são inteligentes o suficiente e as funções curtas sempre são integradas. E nunca faz função longa como inline, a menos que o programador diga para fazer isso.

Eu sei que inline é uma dica ou solicitação para o compilador

Na verdade, inlineé uma ordem para o compilador, ele não tem opções e depois da inlinepalavra-chave torna todo o código embutido. Portanto, você nunca pode usar inlinepalavras-chave e o compilador criará o código mais curto.

Então, quando usar inline?

Para usar se você quiser ter algum código embutido. Eu conheço apenas um exemplo, porque eu o uso em apenas uma situação. É autenticação do usuário.

Por exemplo, eu tenho esta função:

inline bool ValidUser(const std::string& username, const std::string& password)
{
    //here it is quite long function
}

Não importa o tamanho dessa função, quero tê-la como inline, pois dificulta a quebra do meu software.

ST3
fonte
2
inline ainda é uma dica. O compilador pode falhar na linha se considerar que sua função está muito cheia.
precisa saber é o seguinte
Um diz que inline é uma ordem ... o outro diz que é uma dica Alguém fundamentaria sua afirmação para que possamos determinar qual é a verdadeira?
@ user2918461 Eu apoio a instrução inline é apenas uma dica. Este tem sido apoiada por muitos sites e livros
Warhead