Benefícios das funções embutidas em C ++?

254

Quais são as vantagens / desvantagens do uso de funções embutidas no C ++? Vejo que apenas aumenta o desempenho do código que o compilador gera, mas com os compiladores otimizados de hoje, CPUs rápidas, memória enorme etc. (não como nos anos 1980 <onde a memória era escassa e tudo tinha que caber em 100 KB de memória) o que vantagens que eles realmente têm hoje?

Lennie De Villiers
fonte
48
Essa é uma daquelas perguntas em que o conhecimento comum está errado. Todos responderam com a resposta padrão Comp Sci. (Inlining economiza custos com chamadas de funções, mas aumenta o tamanho do código). Lixo. Ele fornece um mecanismo simples para o compilador aplicar mais OPTIMIZAÇÕES.
Martin York
37
Essa é uma daquelas respostas que se apresentam como comentários. Se você não gostar de nenhuma das respostas postadas, poste sua própria resposta e veja como ela vai.
Dave Van den Eynde
10
A base desta questão é falha. As funções embutidas em C ++ têm pouco a ver com compiladores embutidos durante a compilação. É lamentável que inlineseja uma palavra-chave c ++ e que inlining seja uma técnica de otimização de compilador. Veja esta pergunta " quando devo escrever a palavra-chave inlinepara uma função / método " para obter a resposta correta.
Deft_code 24/03/11
3
@JoseVega Seu link foi massacrado - o link atual é exforsys.com/tutorials/c-plus-plus/inline-functions.html

Respostas:

143

As funções embutidas são mais rápidas porque você não precisa ativar / desativar as coisas na pilha, como parâmetros e endereço de retorno; no entanto, ele torna seu binário um pouco maior.

Isso faz uma diferença significativa? Não é notavelmente suficiente no hardware moderno para a maioria. Mas pode fazer a diferença, o que é suficiente para algumas pessoas.

Marcar algo como inline não garante que ele seja inline. É apenas uma sugestão para o compilador. Às vezes, não é possível, como quando você tem uma função virtual ou quando há recursão envolvida. E, às vezes, o compilador escolhe não usá-lo.

Pude ver uma situação como essa fazendo uma diferença detectável:

inline int aplusb_pow2(int a, int b) {
  return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
    for(int b = 0; b < 900000; ++b)
        aplusb_pow2(a, b);
Brian R. Bondy
fonte
26
Como eu suspeitava, inline não faz diferença para o exposto acima. Compilado com o gcc 4.01. Versão 1 forçada a usar inlining: 48.318u 1.042s 5: 51.39 99.4% 0 + 0k 0 + 0io 0pf + 0w Versão 2 forçada sem inlining 348.311u 1.019s 5: 52.31 99.1% 0 + 0k 0 + 0io 0pf + 0w Isso é Um bom exemplo de que o conhecimento comum está errado.
Martin York
36
Embora a chamada em si realmente importe, esse é apenas o pequeno ganho que você obtém usando o inline. O maior ganho é que o compilador agora vê onde os ponteiros não se alternam, onde as variáveis ​​do chamador terminam no chamado e assim por diante. Portanto, a seguinte otimização é o que importa mais.
Johannes Schaub - litb
31
Provavelmente não faz diferença nesse trecho, pois o resultado da função nunca é usado e a função não tem efeitos colaterais. Vemos um ganho de desempenho mensurável na inserção no processamento de imagens.
plinth
4
A razão para não haver diferença pode ser que o compilador possa alinhar por conta própria; ou que o código seja pequeno para que não haja problemas de pré-busca de código.
Einpoklum
3
@einpoklum O compilador pode até otimizar o loop inteiro por causa disso.
noɥʇʎԀʎzɐɹƆ
197

Vantagens

  • Ao incluir o código onde for necessário, seu programa passará menos tempo na chamada de função e retornará as peças. Ele deve acelerar o seu código, mesmo que ele seja maior (veja abaixo). Inlining accessors trivial poderia ser um exemplo de inlining eficaz.
  • Marcando-o como embutido, você pode colocar uma definição de função em um arquivo de cabeçalho (ou seja, pode ser incluído em várias unidades de compilação, sem o reclamante do reclamante)

Desvantagens

  • Isso pode aumentar seu código (ou seja, se você usar inline para funções não triviais). Como tal, poderia provocar paginação e derrotar otimizações do compilador.
  • Ele quebra levemente seu encapsulamento porque expõe o processamento interno do objeto (mas todos os membros "particulares" também). Isso significa que você não deve usar inlining em um padrão PImpl.
  • Ele quebra levemente o encapsulamento 2: o inlining C ++ é resolvido no momento da compilação. O que significa que, se você alterar o código da função embutida, será necessário recompilar todo o código para garantir que ele seja atualizado (pelo mesmo motivo, evito valores padrão para os parâmetros de função)
  • Quando usado em um cabeçalho, ele aumenta seu arquivo de cabeçalho e, portanto, dilui informações interessantes (como a lista de métodos de uma classe) com código com o qual o usuário não se importa (esse é o motivo pelo qual declaro funções embutidas em um classe, mas a definirá em um cabeçalho após o corpo da classe e nunca dentro do corpo da classe).

Magia embutida

  • O compilador pode ou não incorporar as funções marcadas como incorporadas; também pode decidir incorporar funções não marcadas como incorporadas no momento da compilação ou vinculação.
  • O Inline funciona como uma cópia / pasta controlada pelo compilador, que é bem diferente de uma macro de pré-processador: a macro será forçada, poluirá todos os espaços de nomes e códigos, não será facilmente depurável e será feita mesmo se o compilador o tivesse considerado ineficiente.
  • Todo método de uma classe definido dentro do corpo da própria classe é considerado "embutido" (mesmo que o compilador ainda possa decidir não incorporá-lo)
  • Métodos virtuais não devem ser inlináveis. Ainda assim, às vezes, quando o compilador pode ter certeza do tipo do objeto (ou seja, o objeto foi declarado e construído dentro do mesmo corpo da função), mesmo uma função virtual será incorporada porque o compilador sabe exatamente o tipo do objeto.
  • Os métodos / funções do modelo nem sempre estão alinhados (a presença deles em um cabeçalho não os torna alinhados automaticamente).
  • O próximo passo após "inline" é a metaprogramação de modelos. Ou seja, "incorporando" seu código no tempo de compilação, às vezes, o compilador pode deduzir o resultado final de uma função ... Portanto, um algoritmo complexo às vezes pode ser reduzido a um tipo de return 42 ;instrução. Isto é para mim inlining extremo . Isso acontece raramente na vida real, aumenta o tempo de compilação, não incha o seu código e o torna mais rápido. Mas, como o graal, não tente aplicá-lo em qualquer lugar, porque a maioria dos processos não pode ser resolvida dessa maneira ... Ainda assim, isso é legal ...
    :-p
paercebal
fonte
você disse que quebra um pouco o seu encapsulamento. Você poderia explicar usando um exemplo?
Destructor
6
@PravasiMeet: É C ++. Digamos que você entregue uma biblioteca compartilhada / DLL a um cliente, que é compilado. A função inline foo, usando a variável membro X e fazendo o trabalho Y, será incorporada no código do cliente. Digamos que seja necessário entregar uma versão atualizada da sua DLL onde você alterou a variável de membro para Z e adicione um trabalho YY além do trabalho Y. O cliente apenas copia a DLL em seu projeto e BOOM, porque o código de foo no código binário deles não é o código atualizado que você escreveu ... Apesar do cliente não ter acesso legal ao seu código privado, o inlining o torna bastante "público".
paercebal
@paercebal Em relação ao penúltimo ponto final, você pode dar um exemplo de quando um modelo de função não está embutido? Eu pensei que eles estavam sempre alinhados, embora eu não tenha uma referência útil agora (um teste simples parece confirmar isso).
Konrad Rudolph
@KonradRudolph Em n4594 vejo: 3.2/6: There can be more than one definition of [..] inline function with external linkage [..] non-static function template. Em 5.1.5 / 6 For a generic lambda, the closure type has a public inline function call operator member template. E 7.1.2/2: the use of inline keyword is to declare an inline functiononde é uma sugestão alinhar o corpo da função no ponto de chamada. Assim, concluo que, mesmo se eles podem se comportar as mesmas funções, inline e modelos de função são ainda, noções separadas ortogonais que podem ser misturados (ou seja, em linha modelo de função)
paercebal
encapsulamento está quebrado? Como assim? o encapsulamento é para a programação dos indivíduos, não para os objetos reais na memória. nesse ponto, ninguém se importa. Mesmo se você distribuir uma biblioteca, o compilador pode optar por incorporar ou não fazê-lo sozinho. portanto, no final, quando você obtém uma nova lib, basta recompilar tudo o que usa as funções e objetos dessa biblioteca.
FalcoGer 02/04/19
42

No arcaico C e C ++, inlineé como register: uma sugestão (nada mais que uma sugestão) para o compilador sobre uma possível otimização.

No C ++ moderno, inlineinforma ao vinculador que, se várias definições (não declarações) forem encontradas em diferentes unidades de conversão, elas serão iguais e o vinculador poderá manter uma livremente e descartar todas as outras.

inline é obrigatório se uma função (não importa quão complexa ou "linear") for definida em um arquivo de cabeçalho, para permitir que várias fontes a incluam sem obter um erro de "definição múltipla" pelo vinculador.

As funções de membro definidas dentro de uma classe são "inline" por padrão, assim como as funções de modelo (em contraste com as funções globais).

//fileA.h
inline void afunc()
{ std::cout << "this is afunc" << std::endl; }

//file1.cpp
#include "fileA.h"
void acall()
{ afunc(); }

//main.cpp
#include "fileA.h"
void acall();

int main()
{ 
   afunc(); 
   acall();
}

//output
this is afunc
this is afunc

Observe a inclusão de fileA.h em dois arquivos .cpp, resultando em duas instâncias de afunc(). O vinculador descartará um deles. Se não inlinefor especificado, o vinculador irá reclamar.

Emilio Garavaglia
fonte
16

Inlining é uma sugestão para o compilador que é livre para ignorar. É ideal para pequenos pedaços de código.

Se sua função está embutida, ela é basicamente inserida no código em que a chamada de função é feita, em vez de realmente chamar uma função separada. Isso pode ajudar na velocidade, pois você não precisa fazer a chamada real.

Ele também auxilia as CPUs no pipelining, pois elas não precisam recarregar o pipeline com novas instruções causadas por uma chamada.

A única desvantagem é o aumento do tamanho binário, mas, desde que as funções sejam pequenas, isso não importará muito.

Eu costumo deixar esse tipo de decisão para os compiladores hoje em dia (bem, os inteligentes de qualquer maneira). As pessoas que os escreveram tendem a ter um conhecimento muito mais detalhado das arquiteturas subjacentes.

paxdiablo
fonte
12

A função embutida é a técnica de otimização usada pelos compiladores. Pode-se simplesmente acrescentar palavra-chave inline ao protótipo de função para tornar uma função inline. A função embutida instrui o compilador a inserir o corpo completo da função sempre que essa função foi usada no código.

Vantagens: -

  1. Não requer sobrecarga de chamada de função.

  2. Ele também economiza sobrecarga de variáveis ​​push / pop na pilha, enquanto chama funções.

  3. Ele também economiza a sobrecarga da chamada de retorno de uma função.

  4. Aumenta a localidade de referência utilizando o cache de instruções.

  5. Após o alinhamento, o compilador também pode aplicar a otimização intra-processual, se especificado. Este é o mais importante, desta maneira o compilador agora pode se concentrar na eliminação de código morto, pode dar mais ênfase à previsão de ramificação, eliminação de variável de indução etc.

Para saber mais, pode-se seguir este link http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html

TSS
fonte
4
1) É uma sugestão, não uma instrução 2) pode causar mais erros de cache por causa do aumento no tamanho do código, se uma função comumente utilizado é embutido um monte
Flexo
3
O link no final é seu blog pessoal? Se for, você deve declarar como tal, caso contrário, parece spam.
Flexo
6

Eu gostaria de acrescentar que as funções embutidas são cruciais quando você está criando uma biblioteca compartilhada. Sem a função de marcação embutida, ela será exportada para a biblioteca no formato binário. Ele também estará presente na tabela de símbolos, se exportado. Por outro lado, funções embutidas não são exportadas, nem para os binários da biblioteca nem para a tabela de símbolos.

Pode ser crítico quando a biblioteca se destina a ser carregada em tempo de execução. Também pode atingir bibliotecas compatíveis com binários. Nesses casos, não use inline.

doc
fonte
@ Johnsyweb: leia minha resposta com atenção. O que você disse é verdade quando você está construindo um executável. Mas o compilador não pode simplesmente ignorar inlineao criar uma biblioteca compartilhada!
doc
4

Durante a otimização, muitos compiladores incorporarão funções embutidas, mesmo que você não as tenha marcado. Geralmente, você só precisa marcar as funções como inline se souber algo que o compilador não sabe, pois geralmente ele próprio pode tomar a decisão correta.

Devrin
fonte
Muitos compiladores também não farão isso, o MSVC, por exemplo, não fará isso, a menos que você o
solicite
4

inlinepermite colocar uma definição de função em um arquivo de cabeçalho e #includeesse arquivo de cabeçalho em vários arquivos de origem sem violar a regra de uma definição.

Ferruccio
fonte
3

De um modo geral, hoje em dia, com qualquer compilador moderno preocupado em incluir algo é uma perda de tempo. O compilador realmente deve otimizar todas essas considerações para você por meio de sua própria análise do código e sua especificação dos sinalizadores de otimização passados ​​para o compilador. Se você se preocupa com a velocidade, diga ao compilador para otimizar a velocidade. Se você se preocupa com o espaço, diga ao compilador para otimizar o espaço. Como outra resposta mencionada, um compilador decente entrará automaticamente em linha automaticamente se realmente fizer sentido.

Além disso, como outros já declararam, o uso em linha não garante nada em linha. Se você quiser garantir, precisará definir uma macro em vez de uma função embutida para fazê-lo.

Quando alinhar e / ou definir uma macro para forçar a inclusão? - Somente quando você tiver um aumento comprovado e necessário na velocidade de uma seção crítica do código que afeta o desempenho geral do aplicativo.

Tall Jeff
fonte
… Se você se preocupa com espaço, diga ao compilador para otimizar espaço - dizer ao compilador para otimizar velocidade pode resultar em binários menores com C ++ e C. da mesma forma, dizer ao compilador para otimizar espaço pode resultar em execução mais rápida. Agora, esses recursos nem sempre funcionam como anunciados. os seres humanos têm a capacidade de entender alguns aspectos de seu programa melhor do que as interpretações generalizadas de um compilador (que também devem permanecer utilizáveis ​​rapidamente). intervenção humana não precisa ser uma coisa ruim.
justin
3

Não se trata apenas de desempenho. O C ++ e o C são usados ​​para programação incorporada, em cima do hardware. Por exemplo, se você escrever um manipulador de interrupções, precisará garantir que o código possa ser executado de uma só vez, sem que registros e / ou páginas de memória adicionais sejam trocadas. É quando o inline é útil. Bons compiladores fazem alguns "inline" quando a velocidade é necessária, mas "inline" os compele.

JM Stoorvogel
fonte
1

Caiu no mesmo problema com funções embutidas em tão bibliotecas. Parece que funções embutidas não são compiladas na biblioteca. como resultado, o vinculador lança um erro de "referência indefinida", se um executável deseja usar a função embutida da biblioteca. (aconteceu comigo compilando a fonte Qt com o gcc 4.5.

Martin Wilde
fonte
1

Por que não tornar todas as funções embutidas por padrão? Porque é uma troca de engenharia. Existem pelo menos dois tipos de "otimização": acelerar o programa e reduzir o tamanho (pegada de memória) do programa. Inlining geralmente acelera as coisas. Ele elimina a sobrecarga da chamada de função, evitando empurrar e puxar parâmetros da pilha. No entanto, também aumenta a área de memória do programa, porque agora todas as chamadas de funções devem ser substituídas pelo código completo da função. Para tornar as coisas ainda mais complicadas, lembre-se de que a CPU armazena pedaços de memória usados ​​com freqüência em um cache na CPU para acesso ultra-rápido. Se você tornar a imagem de memória do programa grande o suficiente, seu programa não poderá usar o cache de maneira eficiente e, na pior das hipóteses, o inlining poderá reduzir a velocidade do programa.

mehrdad zomorodiyan
fonte
1

Nosso professor de ciência da computação nos incentivou a nunca usar inline em um programa c ++. Quando perguntado por que, ele nos explicou que os compiladores modernos deveriam detectar quando usar o inline automaticamente.

Portanto, sim, o inline pode ser uma técnica de otimização a ser usada sempre que possível, mas aparentemente isso é algo que já é feito para você sempre que é possível incorporar uma função de qualquer maneira.

HumbleWebDev
fonte
5
Infelizmente, seu professor está completamente errado. inlineno C ++ tem dois significados muito distintos - apenas um deles está relacionado à otimização, e seu professor está correto com relação a isso. No entanto, o segundo significado de inlineé frequentemente necessário para satisfazer a Regra de Uma Definição .
Konrad Rudolph
-1

Conclusão de outra discussão aqui:

Existem desvantagens com funções embutidas?

Aparentemente, não há nada de errado em usar funções embutidas.

Mas vale a pena notar os seguintes pontos!

  • O uso excessivo de inlining pode realmente tornar os programas mais lentos. Dependendo do tamanho de uma função, a inclusão de uma função pode aumentar ou diminuir o tamanho do código. A inclusão de uma função acessora muito pequena geralmente diminui o tamanho do código, enquanto a inclusão de uma função muito grande pode aumentar drasticamente o tamanho do código. Nos processadores modernos, códigos menores geralmente são executados mais rapidamente devido ao melhor uso do cache de instruções. - Diretrizes do Google

  • Os benefícios de velocidade das funções embutidas tendem a diminuir à medida que a função aumenta de tamanho. Em algum momento, a sobrecarga da chamada de função torna-se pequena em comparação com a execução do corpo da função e o benefício é perdido - Fonte

  • Existem poucas situações em que uma função embutida pode não funcionar:

    • Para uma função retornando valores; se existir uma declaração de retorno.
    • Para uma função que não retorna nenhum valor; se existir uma instrução loop, switch ou goto.
    • Se uma função é recursiva. -Fonte
  • A __inlinepalavra-chave faz com que uma função seja incorporada apenas se você especificar a opção de otimização. Se a opção otimizar for especificada, se é ou não __inlinerespeitada depende da configuração da opção otimizador em linha. Por padrão, a opção embutida entra em vigor sempre que o otimizador é executado. Se você especificar otimizar, também deverá especificar a opção noinline se desejar que a __inlinepalavra-chave seja ignorada. -Fonte

prakash
fonte
3
Seria verdadeiro se o inline fosse um comando e não uma dica para o compilador. O compilador realmente decide o que incorporar.
Martin York
1
@LokiAstari Eu sei que inline é pedido para compilador. Meu argumento é: Se é uma dica para o compilador, devemos deixá-lo decidir o que é melhor. Por que usar inline de qualquer maneira, mesmo se você usar inline, ainda será o compilador que tomará a decisão final. Também me pergunto que minha Microsoft introduziu _forceinline.
Krishna Oza
@krish_oza: Meu comentário aqui. É sobre esta resposta. A resposta aqui está completamente errada. Como o compilador desconsidera a inlinepalavra - chave para determinar se o código embutido deve ou não ser incorreto. Eles seriam verdadeiros se o compilador usasse a palavra-chave para embutir (é usada apenas para determinar a marcação de várias definições para fins de vinculação).
Martin York