Funções embutidas em C ++. Qual é o objetivo?

19

De acordo com o que li, o compilador não é obrigado a substituir a chamada de função de uma função embutida por seu corpo, mas o fará se puder. Isso me fez pensar: por que temos a palavra em linha se for esse o caso? Por que não tornar todas as funções funções em linha por padrão e deixar o compilador descobrir se pode substituir as chamadas pelo corpo da função ou não?

EpsilonVector
fonte
1
Aqui . Página 6
EpsilonVector 10/01

Respostas:

40

inlineé de C; não era novo no C ++.

Existem palavras-chave C ( registere inline) que foram projetadas para permitir ao programador ajudar na otimização do código. Geralmente, eles são ignorados hoje em dia, pois os compiladores podem se sair melhor na atribuição de registros e decidir quando incorporar funções (na verdade, um compilador pode incorporar ou não uma função em momentos diferentes). A geração de código nos processadores modernos é muito mais complicada do que nos mais determinísticos comuns quando Ritchie estava inventando o C.

O que a palavra significa agora, em C ++, é que ela pode ter várias definições idênticas e precisa ser definida em todas as unidades de tradução que a usam. (Em outras palavras, você precisa garantir que ela possa ser incorporada.) Você pode ter uma inlinefunção em um cabeçalho sem problemas, e as funções de membro definidas em uma definição de classe são automaticamente efetivas inline.

David Thornley
fonte
3
+1 para o histórico de inline.
Tamara Wijsman
11
Tenho certeza de que inlinefoi padronizado em C ++ primeiro, embora já estivesse disponível como uma extensão de fornecedor em C .... sim, parece que foi adicionado ao padrão C em C99.
Ben Voigt
@ Ben Voigt: Você está certo. Eu o encontrei pela primeira vez em C.
David Thornley
5
Lembre-se também de que não apenas o histórico está errado, mas as regras do inlineC99 e posteriores são diferentes das regras inlinedo C ++.
Alf P. Steinbach
27

Originalmente, inlinehavia uma dica muito forte de que as chamadas para a função deveriam ser incorporadas.

Mas o único efeito garantido de inlineé permitir que uma função seja definida (efetivamente de forma idêntica) em várias unidades de conversão, por exemplo, que você coloque a definição em um arquivo de cabeçalho.

Atualmente, alguns compiladores estão muito interessados ​​em seguir a dica embutida, por exemplo, g ++. E alguns compiladores levam isso menos a sério, por exemplo, Visual C ++. Mas todos têm que cumprir a garantia.

É lamentável que esses dois significados - dica de otimização e o que poderíamos chamar de definição descartável no nível do vinculador - residam com a mesma palavra-chave, porque significa que você não pode praticamente ter um sem o outro.

Também é lamentável que inline(ou melhor, uma palavra-chave separada sobre definição descartável) não possa ser aplicada aos dados .

A necessidade de dados descartáveis ​​no nível do vinculador aumentou à medida que os módulos somente de cabeçalho se tornaram mais populares. Por exemplo, muitas sub-bibliotecas do Boost são apenas de cabeçalho.

Para dados, você pode, no entanto, aplicar um pequeno truque com modelos. Defina-o em algum modelo de classe, forneça um typedefparâmetro com modelo void(ou o que seja). Isso ocorre porque a regra de definição única faz uma exceção específica para modelos.

Notas:
¹ inlinevariáveis ​​serão suportadas no C ++ 17 .

Alf P. Steinbach
fonte
1
+1 para obter informações adicionais sobre inline.
Tamara Wijsman
1
" efetivamente identicamente " é realmente uma condição muito complicada, pois as linguagens C ++ mal projetadas tentam fazer com que programadores normalmente competentes e cuidadosos escrevam códigos razoáveis, mas formalmente indefinidos, que usam objetos estáticos - eu me pergunto quantos membros do comitê caem na armadilha.
precisa
6

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 pressionar e extrair parâmetros da pilha. No entanto, também aumenta a área de memória do programa, porque agora toda chamada de função deve ser substituída 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, inlining poderá realmente tornar seu programa lento.

Charles E. Grant
fonte
5
Mas o compilador pode (e faz!) Descobrir isso muito melhor do que o programador em geral. Portanto, este não é um argumento válido.
Konrad Rudolph
" toda chamada de função agora deve ser substituída pelo código completo da função " E a função embutida não é uma função na qual a sequência de chamadas é omitida e onde o código de montagem da função é copiado no local. Uma função embutida é compilada no local, trazendo o código intermediário de alto nível embutido. Isso permite que o compilador trate o corpo da função efetivamente como uma macro limpa (uma macro limpa é aquela que não possui as peculiaridades das macros do pré-processador C), com potencialmente muitas otimizações disponíveis.
curiousguy
3

Para entender “inline”, você precisa entender a história e como era a vida há 20 (e 30) anos atrás.

Estávamos escrevendo código em computadores com pouca memória, portanto não era possível para um compilador processar todo o código que compunha um programa de uma só vez. O compilador também foi muito lento, portanto, você não queria recompilar o código que não havia sido alterado - levando mais de 24 horas (em um computador que custa mais que um carro de ponta) para recompilar todo o código normal em alguns projetos trabalhou em.

Portanto, cada arquivo de código foi compilado separadamente em um arquivo de objeto. Cada arquivo de objeto começou com uma lista de todas as funções que continha, juntamente com o "endereço" da função. Um arquivo de objeto também tinha uma lista de todas as funções que chamava em outros arquivos de objeto, juntamente com o local da chamada.

Um vinculador primeiro lê todos os arquivos de objetos e cria uma lista de todas as funções exportadas, juntamente com o arquivo em que estão e o endereço. Em seguida, ele releria todos os arquivos de objeto, enviando-os para o arquivo de programa, enquanto atualizava todas as chamadas de função “externas” com o endereço da função.

O vinculador não alterou ou otimizou o código de máquina produzido pelo compilador de maneira alguma, a não ser para corrigir referências a chamadas de funções externas. O vinculador fazia parte do sistema operacional e é anterior à maioria dos compiladores. Quando as pessoas escreviam um novo compilador, elas precisavam que ele trabalhasse com os vinculadores atuais e que pudessem vincular aos arquivos de objetos atuais, caso contrário, as chamadas do sistema não poderiam ser feitas.

O compilador só viu o código no arquivo “.c” ou “.cpp” que estava compilando junto com todos os arquivos de cabeçalho incluídos. Portanto, não foi possível fazer nenhuma otimização com base no código em outros arquivos ".c" ou ".cpp".

A palavra-chave “inline” permitiu que o corpo de uma função (método) fosse definido em um arquivo de cabeçalho, permitindo assim que o compilador utilizasse o código da função enquanto compilava o código que a chama. Por exemplo, digamos que você tivesse uma classe de coleção definida no arquivo .cpp, essa classe teria um método "isEmpty", que continha uma linha de código, haveria uma grande aceleração do programa resultante se, em vez de uma chamada para uma função , a chamada de função foi substituída por esta linha.

A palavra-chave “inline” era vista na época como uma maneira “barata e fácil” de permitir o encapsulamento de dados e, ao mesmo tempo, evitar o custo de chamadas de função, sem que muitos programadores tivessem acessado apenas os campos privados do objeto. (Macros onde há uma maneira muito pior de "inserir" o código que era comum na época.)

Atualmente, os “linkers” fazem muita otimização de código e tendem a ser escritos por alguma equipe como compilador. O compilador geralmente verifica se o código está correto e o "compacta", deixando a maior parte da tarefa de criação de código de máquina para o vinculador.

Ian
fonte
2

Vamos ver o que o padrão diz (partes importantes destacadas em negrito):

2. Uma declaração de função com um especificador inline declara uma função inline. 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 ainda devem ser respeitadas.

- Padrão C ++, ISO / IEC 14882: 2003 , 7.1.2 Especificadores de função [dcl.fct.spec]

Portanto, se você quiser ter certeza, leia a documentação do seu compilador.

Incluir tudo é uma má ideia, pois pode resultar em muitos códigos de máquina duplicados ...

Então você terá que saber:

Não há respostas simples: você precisa brincar com ele para ver o que é melhor. Você não se contentar com respostas simplistas como: "Nunca use inlinefunções" ou "Sempre usar inlinefunções" ou "utilização inlinefunções se e somente se a função for inferior a N linhas de código." Essas regras de tamanho único podem ser fáceis de serem anotadas, mas produzirão resultados abaixo do ideal.

- Perguntas frequentes sobre C ++, funções em linha , 9.3 As inlinefunções melhoram o desempenho?

Tamara Wijsman
fonte
1
O que você diz é verdade, mas não é relevante porque, como programador, não é sua decisão entrar ou não na linha. Se você colocar a palavra-chave, poderá ou não obter suas funções incorporadas. Você não coloca a palavra-chave, ainda pode ou não inseri-las. É inútil ter QUALQUER regra para quando você coloca a palavra-chave, com o compilador sendo o único a tomar a decisão.
22711 Kate
Como isso não é relevante se diz exatamente o mesmo que você? Não há respostas simples .
Tamara Wijsman
Não é relevante porque não responde à pergunta dos OPs. Ele não está perguntando "o que o inline faz", ele está perguntando "qual é o sentido". E sua resposta apenas reitera o que todos já sabemos sobre inline.
Davor Ždralo 11/06/11
@ Davor: "Qual é o objetivo?" não adere às perguntas frequentes, por isso estou respondendo às perguntas colocadas na pergunta. Eu estou dizendo o ponto de inlinereiterar o conhecimento, pois o OP parece perder uma parte, e essa é a principal razão pela qual ele não entende. Por um lado para obter um ponto ele precisa ter uma compreensão do que está abaixo desse ponto ...
Tamara Wijsman
Por que diabos não aderiu ao FAQ? O padrão descreve a sintaxe e a semântica da palavra-chave em linha, e a pergunta dos OP é qual é o objetivo, quando deve ser usada, quais problemas ele deve resolver? Não vejo como isso não segue as Perguntas frequentes.
Davor Ždralo
1

Deixe-me dar um bom motivo para usar a palavra-chave inline.

Em um sistema incorporado, como uma impressora de tickets ou sistema menor semelhante. O processador é muito limitado e uma chamada de função (preparando parâmetros de função na pilha, chamada, buscar parâmetros da pilha e colocar de volta a resposta etc.) pode levar vários ms para ser executada, além da própria função.

Digamos que o tempo de chamada é de cerca de 60 ms (apenas para chamadas, e não a função real) e você realiza 50 iterações (loop ou chamadas iterativas em uma árvore).

O tempo para avançar e voltar dessa chamada de função levará 60 * 50 = 3000 (3 segundos).

Se você tem memória, você definitivamente faria um inline para economizar 3 segundos.

Portanto, o inline é basicamente usado quando você precisa de velocidade de execução. Em alguns projetos com os quais me envolvi, o tempo de chamada foi maior que o tempo de execução, uma situação clássica quando usar inline.

Max Kielland
fonte
Eh o que? Colocar inline no seu código não significa que o compilador realmente o inline, e não colocá-lo lá também não significa que não. É apenas uma dica, que é geralmente ignorada pelos compiladores de hoje. Se o compilador achar útil alinhar, então, caso contrário, não o fará. Você não tem controle real lá. O OP já sabe disso, e está perguntando, cito: "Qual é o objetivo?". Quem diabos dá +1 nisso? É enganoso (implicando que a palavra-chave inline impõe o inline) e irrelevante para a pergunta.
Davor Ždralo 11/06
2
@Davor Ždralo Não precisa ser rude. Eu interpretei a pergunta como POR QUE devo usar inline? Meu objetivo era mostrar que você pode obter o código mais rapidamente com o custo da memória. Cada compilador pode lidar com a palavra-chave inline diferente, então você precisa verificar a documentação, que eu não mencionei. Como você nem sempre pode pagar pela sobrecarga de memória adicional criada pelas linhas, é útil "orientar" quando usá-lo. Eu também não disse que as linhas são aplicadas. Alguém achou útil essa resposta e votou em +1. Respeite que outros possam ter outro nível de experiência e encontrar algo útil e trivial.
Max Kielland