Встроенный метод C ++ в сборке - PullRequest
0 голосов
/ 08 января 2019

Позволяет иметь небольшой класс под названием myClass . Мне было интересно, как выглядит разница в .asm, когда метод встроенный или нет. Я сделал две программы, с и без ключевого слова inline в файле cpp, но вывод .asm был таким же. Я знаю, что inline - всего лишь подсказка для компилятора, и с большой вероятностью я стал жертвой оптимизации, но возможно ли увидеть разницу на небольшом примере cpp для встроенного и не встроенного метода в асм?

ч:

#ifndef CLASS_H
#define CLASS_H

class myClass{
private:
  int a;
public:
  int getA() const;
};

#endif

касты:

#include <class.h>
inline int myCLass::getA() const{
  return a;
};

Основные: * * тысяча двадцать-одна

#include "class.h"

int main(){
    myClass a;
    a.getA();
    return 0;
}

НКА:

gcc -S -O0 main.cpp

вывод asm в обоих случаях:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    leaq    -8(%rbp), %rdi
    movl    $0, -4(%rbp)
    callq   __ZNK7myClass4getAEv
    xorl    %ecx, %ecx
    movl    %eax, -12(%rbp)         ## 4-byte Spill
    movl    %ecx, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function

.subsections_via_symbols

Ответы [ 4 ]

0 голосов
/ 10 января 2019

gcc -O0 не включает -finline-functions, поэтому, даже если функции находятся в одном файле, он не будет пытаться. См. Также Почему этот класс-оболочка C ++ не выделен? . (Не пытайтесь использовать __attribute__((always_inline)): вы получите встраивание, ничего не оптимизируется.

С помощью gcc -O3 -fwhole-program *.cpp вы можете сделать что-то встроенное, чтобы включить встраивание в исходные файлы. (Независимо от того, были ли они объявлены inline или нет, компилятору решать, что лучше).

Суть inline состоит в том, чтобы сообщить компилятору, что ему не нужно выдавать отдельное определение функции, если он решит встроить ее во всех вызывающих. (Поскольку определение, а не просто объявление этой функции будет появляться во всех единицах перевода, которые ее используют. Поэтому, если какой-то другой файл решит не вставлять его, определение может быть передано туда.)

Современные компиляторы все еще используют свою обычную эвристику, чтобы решить, стоит ли вставлять или нет. например большая функция с несколькими вызывающими, вероятно, не будет встроена, чтобы избежать раздувания кода. static сообщает компилятору, что никакой другой модуль перевода не может видеть функцию, поэтому, если в этом файле есть только один вызывающий объект, он, скорее всего, будет там встроен. (Если у вас есть большая функция, плохая идея сделать ее static inline. Вы получите копию определения в каждом файле, где она не встроена, и слишком агрессивная вставка. Для небольшой функции, которая, вероятно, будет для повсеместного встраивания, вам, вероятно, все равно следует просто использовать inline, а не static inline, поэтому в случае, если что-либо займет адрес функции, для всех файлов будет использоваться только одно определение. inline говорит компоновщику объединить повторяющиеся определения функции вместо ошибок. Это поведение является одной из наиболее важных частей того, что на самом деле делает inline, а не фактической подсказкой компилятору, что вы хотите, чтобы он был встроен.)


gcc -fwhole-program (со всеми исходными файлами в одной командной строке) дает компилятору достаточно информации, чтобы принять все эти решения самостоятельно. Он может видеть, есть ли у функции только один вызывающий объект во всей программе, и встроить его вместо создания отдельного определения, настройки arg и call.

.

gcc -flto позволяет оптимизировать время соединения, аналогично целой программе, но не требует всех файлов .cpp в командной строке одновременно. Вместо этого он сохраняет код GIMPLE в файлах .o и завершает оптимизацию во время ссылки.

0 голосов
/ 08 января 2019

Ключевое слово inline имеет мало общего с указанием компилятору встроенных вызовов функции. inline позволяет функции быть определенной в заголовке.

Использование ключевого слова inline в определении функции позволяет определять функцию в нескольких единицах перевода без нарушения правила одного определения, если все определения идентичны.

Определение встроенной функции в заголовке может помочь встроенным вызовам функции компилятора, позволяя отображать полное определение в нескольких единицах перевода, но это все, что ключевое слово inline имеет отношение к встроенным вызовам.

0 голосов
/ 08 января 2019

Вы не дали компилятору шанс встроить myClass::getA() (как объяснено в другом ответе). Чтобы сравнить встроенные и внешние методы, сравните использование getA() и getAlt() в приведенном ниже коде

// file.h    
class myClass
{
    int a=1;
public:
    int getA() const { return a; }   // implicitly inline
    int getAlt() const;
};

// file.cc
#include "file.h"
int myClass::getAlt() const
{ return a; }

// main.cc
#include "file.h"
int main()
{
    myClass x;
    return x.getA() - x.getAlt();
}

Вы можете использовать полную оптимизацию, кроме межпроцедурной оптимизации (ipo; поскольку это может позволить компилятору даже встроить myClass::getAlt()).

0 голосов
/ 08 января 2019

Чтобы функция была надежно встроена, полное определение функции должно быть доступно в единице перевода, в которой появляется вызов.

Поскольку определение myClass::getA() не отображается для main.c, функция не может быть встроенной. Он может быть встроен в любые вызовы, которые появились в myClass.cpp, но у вас их нет.

Чтобы включить эту функцию, вам нужно объявить ее как inline в заголовочном файле и включить ее определение, например,

class myClass {
  …
  inline int getA() const { return a; }
  …
}
...