Wbudowane funkcje w C ++. Jaki jest sens?

19

Według tego, co czytam, kompilator nie jest zobowiązany do zastąpienia wywołanie funkcji inline funkcji z jej ciała, ale zrobi to, jeśli to możliwe. To dało mi thinking- dlaczego mamy słowo inline, czy tak jest w istocie? Dlaczego nie wszystkie funkcje Funkcja inline domyślnie i niech postać kompilatora, czy można go zastąpić połączenia z ciałem funkcji, czy nie?

EpsilonVector
źródło
1
Tutaj . Strona 6
EpsilonVector 10.01.11

Odpowiedzi:

40

inlinewynosi od C; to nie był nowy w C ++.

Istnieją słowa kluczowe C ( registeri inline), które zostały zaprojektowane, aby umożliwić programistom pomoc w optymalizacji kodu. Są one obecnie na ogół ignorowane, ponieważ kompilatory mogą lepiej radzić sobie z przypisywaniem rejestrów i podejmowaniem decyzji, kiedy należy wstawić funkcje (w rzeczywistości kompilator może wstawić lub nie wstawić funkcji w różnych momentach). Generowanie kodu na nowoczesnych procesorach jest znacznie bardziej skomplikowane niż na bardziej deterministyczny ones Common kiedy Ritchie był wymyślając C.

Jakie środki słowo teraz, w C ++, jest to, że może on mieć wiele identycznych definicji, i musi być zdefiniowane w każdej jednostce tłumaczeniowej, która go używa. (Innymi słowy, trzeba upewnić się, że może to być inlined.) Można mieć inlinefunkcję w nagłówku bez żadnych problemów, a funkcje składowe określone w definicji klasy są automatycznie skutecznie inline.

David Thornley
źródło
3
+1 za historię inline.
Tamara Wijsman
11
Jestem pewien, że inlinebył standaryzowany w C ++ pierwszy, choć było już dostępne jako rozszerzenie sprzedawca w C .... Tak, wydaje się, że dodano do standardu C w C99.
Ben Voigt
@Ben Voigt: Masz rację. Po raz pierwszy spotkałem go w C.
David Thornley
5
Również należy pamiętać, że nie tylko jest historią źle, ale zasady inlinew C99 i późniejsze różnią się od zasad inlinew C ++.
Alf P. Steinbach
27

Początkowo inlinebyła bardzo silna wskazówka, że wywołania funkcji należy inlined.

Ale tylko gwarantuje efekt inlinejest umożliwienie funkcją być zdefiniowana (efektywnie identycznie) w wielu jednostek tłumaczeniowych, np się umieszczenie definicji w pliku nagłówka.

Obecnie niektóre kompilatory są bardzo chętnie po nutą inline np g ++. I niektóre kompilatory zabrać go mniej poważnie, na przykład Visual C ++. Ale wszyscy muszą przestrzegać gwarancji.

Szkoda, że ​​te dwa znaczenia - Optymalizacja i podpowiedź, co moglibyśmy nazwać poziomu łącznika discardable definicja - przebywania z tego samego słowa kluczowego, ponieważ oznacza to, że praktycznie nie można mieć jednego bez drugiego.

To niefortunne, że również inline(albo lepiej, oddzielne słów kluczowych o discardable definicji) ¹cannot być zastosowane do danych .

Zapotrzebowanie na poziomie łącznika discardable danych wzrosła jako moduły header tylko stają się coraz bardziej popularne. Na przykład, wiele sub-Boost biblioteki są tylko nagłówek.

Dla danych można jednak zastosować mały trick z szablonami. Zdefiniować go w jakiś szablon klasy zapewnić typedefparametrem szablonu void(lub cokolwiek). To dlatego, że jedna definicja reguły czyni wyjątek specyficzny dla szablonów.

Uwagi:
¹ inlinezmienne będą obsługiwane C ++ 17 .

Alf P. Steinbach
źródło
1
+1 za dodatkowe informacje inline.
Tamara Wijsman
1
skutecznie identycznie ” jest w rzeczywistości bardzo trudnym warunkiem, ponieważ bardzo źle zaprojektowane języki C ++ bardzo ciężko próbują zmusić normalnie kompetentnych i ostrożnych programistów do napisania rozsądnego, ale formalnie niezdefiniowanego kodu, który wykorzystuje obiekty statyczne - zastanawiam się, ilu członków komitetu wpadło w pułapkę
curiousguy
6

Dlaczego nie włączyć domyślnie wszystkich funkcji? Ponieważ jest to kompromis inżynieryjny. Istnieją co najmniej dwa rodzaje „optymalizacji”: przyspieszenie programu i zmniejszenie rozmiaru (powierzchni pamięci) programu. Inlining ogólnie przyspiesza. To pozbywa napowietrznej wywołania funkcji, unikając popychanie parametrów potem pociągając ze stosu. Jednak powoduje to również zwiększenie pamięci programu, ponieważ każde wywołanie funkcji musi teraz zostać zastąpione pełnym kodem funkcji. Aby jeszcze bardziej skomplikować sprawę, pamiętaj, że procesor przechowuje często używane fragmenty pamięci w pamięci podręcznej procesora, zapewniając ultra szybki dostęp. Jeśli sprawisz, że obraz pamięci programu będzie wystarczająco duży, Twój program nie będzie w stanie efektywnie korzystać z pamięci podręcznej, aw najgorszym przypadku wbudowanie może spowolnić program.

Charles E. Grant
źródło
5
Ale puszka kompilator (i nie!) Postać ta się znacznie lepiej niż programista w ogóle. To nie jest prawidłowy argument.
Konrad Rudolph
każde wywołanie funkcji musi teraz zostać zastąpione pełnym kodem funkcji ”. Funkcja wbudowana nie jest funkcją, w której pominięto sekwencję wywołań i gdzie skopiowano kod zestawu funkcji. Inline funkcji zestawione w miejscu, przez doprowadzenie wysokiego poziomu kodu pośredniego rolkach. Dzięki temu kompilator może skutecznie traktować ciało funkcji jako czyste makro (czyste makro to takie, które nie ma dziwactw makr preprocesora C), z potencjalnie wieloma dostępnymi optymalizacjami.
ciekawy
3

Aby zrozumieć „inline” trzeba zrozumieć historię i jak wyglądało życie 20 (i 30) lat temu.

Pisaliśmy kod na komputerach, które miały mało pamięci, więc kompilator nie był w stanie przetworzyć całego kodu tworzącego program za jednym razem. Kompilator działał również bardzo wolno, więc nie trzeba było ponownie kompilować kodu, który się nie zmienił - przejęcie 24 godzin (na komputerze kosztującym więcej niż samochód wyższej klasy) w celu rekompilacji całego kodu było normalne dla kilku projektów I pracował.

Dlatego każdy plik kodu został osobno skompilowany w pliki obiektowe. Każdy plik obiektowy zaczynał się listą wszystkich zawartych w nim funkcji wraz z „adresem” funkcji. Plik obiektowy zawierał także listę wszystkich funkcji, które wywołał w innych plikach obiektowych wraz z lokalizacją wywołania.

Łącznik najpierw przeczytać wszystkie pliki obiektów i zbudować listę wszystkich funkcji one eksportowane wraz z plikiem byli i nie ma adresu. Następnie ponownie odczytałby wszystkie pliki obiektowe, wyprowadzając je do pliku programu, jednocześnie aktualizując wszystkie wywołania funkcji „zewnętrznej” o adres funkcji.

Linker nie zmienił ani nie zoptymalizował kodu maszynowego wygenerowanego przez kompilator w żaden inny sposób niż w celu poprawienia odniesień do wywołań funkcji zewnętrznych. Linker był częścią systemu operacyjnego i wyprzedza większość kompilatorów. Kiedy ludzie pisali nowy kompilator, potrzebowali go do pracy z bieżącymi linkerami i do możliwości łączenia się z bieżącymi plikami obiektowymi, w przeciwnym razie wywołanie systemowe nie byłoby możliwe.

Kompilator widział tylko kod w kompilowanym pliku „.c” lub „.cpp” wraz ze wszystkimi dołączonymi plikami nagłówkowymi. Nie mógł więc dokonać żadnej optymalizacji opartej na kodzie w innych plikach „.c” lub „.cpp”.

Słowo kluczowe „inline” pozwoliło na zdefiniowanie treści funkcji (metody) w pliku nagłówkowym, umożliwiając kompilatorowi korzystanie z kodu funkcji podczas kompilacji kodu, który ją wywołuje. Na przykład powiedzieć, że miał klasę zbiórki określony w pylników plik .cpp, ta klasa będzie miała „isEmpty” metodę, która zawierała jedną linię kodu, nie byłoby wielkim przyspieszenie wynikowego programu, jeśli zamiast wywołania funkcji , wywołanie funkcji zastąpiono tej jednej linii.

Słowo kluczowe „inline” było wówczas postrzegane jako „tani i łatwy” sposób na enkapsulację danych przy jednoczesnym uniknięciu kosztów wywołań funkcji, bez których wielu programistów miałoby dostęp do prywatnych pól obiektu. (Makra, w których znacznie gorszy sposób „wstawiania” kodu jest powszechny w tym czasie).

Obecnie „linkery” zajmują się optymalizacją kodu i zwykle są pisane przez jakiś zespół jako kompilator. Kompilator często po prostu sprawdza kod jest poprawny i „okłady”, pozostawiając większość zadanie tworzenia kodu maszynowego do łącznika.

Ian
źródło
2

Zobaczmy, co standardowe mówi (podświetlony ważnych części pogrubione):

2. Oświadczenie o funkcji inline specyfikatorem deklaruje inline funkcję. Inline specyfikator wskazuje realizacji że rolki podstawienie ciała funkcji w punkcie połączenia jest preferowane do zwykłego mechanizmu wywołania funkcji. Implementacja nie jest wymagane, aby wykonać tę substytucję inline w punkcie poboru; Jednakże, nawet jeśli takie podstawienie inline jest pominięte, inne zasady funkcji inline powinna nadal być przestrzegane.

- C ++ standard ISO / IEC 14882: 2003 , 7.1.2 Specyfikatory funkcyjne [dcl.fct.spec]

Tak więc, jeśli chcesz mieć pewność, należy zapoznać się z dokumentacją swojego kompilatora.

Inline wszystko jest złym pomysłem, ponieważ może to spowodować wiele powielanego kodu maszynowego ...

Musisz więc wiedzieć:

Nie ma prostych odpowiedzi: Musisz grać z nim, aby zobaczyć, co jest najlepsze. Czy nie godzić się na uproszczonych odpowiedzi jak „Never użycia inlinefunkcji” lub „zawsze używaj inlinefunkcji” lub „użytkowania inlinefunkcji, wtedy i tylko wtedy, gdy funkcja jest mniej niż N linii kodu.” Te jeden rozmiar dla wszystkich zasady mogą być łatwo napisać, ale będą przynosić rezultaty sub-optymalne.

- C ++ FAQ, funkcje wbudowane , 9.3 Czy inlinefunkcje poprawiają wydajność?

Tamara Wijsman
źródło
1
To, co mówisz, jest prawdą, ale nie ma znaczenia, ponieważ jako programista to nie twoja decyzja, czy wstawiać się, czy nie. Można umieścić słowa kluczowego, to może lub nie może dostać twoje funkcje inline. Nie należy umieszczać słowa kluczowego, nadal może lub nie może uzyskać je inlined. Po umieszczeniu słowa kluczowego nie ma sensu ŻADNE reguły, ponieważ kompilator jest tym, który faktycznie podejmuje decyzję.
Kate Gregory
Jak nie ma to znaczenia, czy to mówi dokładnie tak samo jak ty? Nie ma prostych odpowiedzi .
Tamara Wijsman
Nie ma to znaczenia, ponieważ nie odpowiada na pytanie PO. Nie pyta „co robi inline”, pyta „o co chodzi”. A twoja odpowiedź po prostu przypomina to, co wszyscy wiemy o inline.
Davor Ždralo
@Davor: „Jaki jest sens?” nie stosuje się do FAQ, więc odpowiadam na pytania zadane w pytaniu. Mówię punkt inline, potwierdzając wiedzę jak PO wydaje się stracić część więc co jest powodem rdzeń dlaczego nie dostaje punkt. Dla jednego, aby uzyskać punkt on musi mieć wiedzę o tym, co znajduje się pod tym momencie ...
Tamara Wijsman
Dlaczego, do cholery, nie byłoby stosować się do FAQ? Norma opisuje składnię i semantykę słowa kluczowego inline, a PO pytanie brzmi: co jest celem, kiedy powinien on być stosowany, jakie problemy Ma to rozwiązać? Nie widzę, w jaki sposób, który nie przywiera do FAQ.
Davor Ždralo
1

Podam dobry powód za pomocą słowa kluczowego inline.

W systemie wbudowanym, takim jak drukarka biletów lub podobny mniejszy system. Procesor jest bardzo ograniczona i wywołanie funkcji (przygotowanie parametrów funkcji na stosie, zadzwoń, params pobrać ze stosu i umieścić z powrotem odebrać etc ..) może potrwać kilka ms do wykonania, obok samej funkcji.

Powiedzmy, że czas połączenia wynosi około 60 ms (tylko do wywołania, a nie rzeczywistej funkcji) i masz 50 iteracji (pętli lub iteracyjnych wywołań w drzewie).

Czas po prostu przejść do przodu i do tyłu z tego połączenia funkcja będzie miała 60 * 50 = 3000 (3 sekund).

Jeśli masz pamięć to na pewno zrobić, aby zapisać inline 3 sekundy.

Inline są więc zasadniczo używane, gdy potrzebujesz prędkości wykonania. W niektórych projektach, w które byłem zaangażowany, czas wywoływania był dłuższy niż czas wykonania, co jest klasyczną sytuacją, gdy należy używać funkcji inline.

max Kielland
źródło
Eh, co? Wstawienie inline w kodzie nie oznacza, że ​​kompilator faktycznie go wstawi, a nie wstawienie go również nie oznacza, że ​​nie będzie. To tylko wskazówka, która jest obecnie ignorowana przez dzisiejsze kompilatory. Jeśli kompilator uzna, że ​​przydatne jest wstawienie, to w przeciwnym razie nie będzie. Nie masz realną kontrolę tam. OP już to wie i pyta: „O co chodzi?”. Kto do cholery daje +1 na ten temat? Jest to zarówno mylące (sugerujące, że słowo kluczowe wbudowane wymusza wstawianie), jak i nie ma znaczenia dla pytania.
Davor Ždralo
2
@Davor Ždralo Nie trzeba być niegrzeczny. Zinterpretowałem pytanie jako DLACZEGO powinienem używać inline? Chodziło mi o to, aby pokazać, że można uzyskać kod szybciej, kosztem pamięci. Każdy kompilator może obsługiwać słowo kluczowe inline inaczej, więc musisz sprawdzić dokumentację, o której nie wspomniałem. Ponieważ nie zawsze możesz sobie pozwolić na dodatkowe nakładki pamięci, które są tworzone, przydatne jest „wskazanie”, kiedy z nich korzystać. Ja również nie powiedzieć, że inlines są egzekwowane. Ktoś uznał tę odpowiedź za przydatną i zagłosował +1, szanuj, że inni mogą mieć inny poziom doświadczenia i znaleźć coś trywialnego pożytecznego.
maksymalna Kielland