Załóżmy, że mam następujący kod:Zaproponuj do kompilatora do selektywnego funkcja inline wzywa
struct Foo {
void helper() { ... }
void fast_path() { ...; helper(); ... }
void slow_path1() { ...; helper(); ... }
void slow_path2() { ...; helper(); ... }
};
Sposób fast_path()
jest wydajność krytyczna i tak każdy (rozsądny) należy dołożyć starań, aby go tak szybko, jak to możliwe. Metody nie są krytyczne z punktu widzenia wydajności.
Z mojego zrozumienia, typowy kompilator może spojrzeć na ten kod i zadecydować o inline helper()
jeśli jest na tyle skomplikowane, aby zmniejszyć całkowity rozmiar instrukcji, jak helper()
dzielone między funkcjami wielu metod. Ten sam kompilator może wstawiać helper()
, jeśli metody wolnej ścieżki nie istnieją.
biorąc pod uwagę nasze żądane właściwości użytkowe, chcemy kompilator do inline wywołanie helper()
wewnątrz fast_path()
, ale wolą domyślne zachowanie kompilatora w slow_path1()
i slow_path2()
.
Jednym rozwiązaniem jest mieć definicje funkcji zwolnionym ścieżka i wezwanie do fast_path()
żywo w oddzielnych jednostkach kompilacji, tak, że kompilator nie widzi wykorzystanie helper()
dzieloną z fast_path()
. Zachowanie tego rozdziału wymaga szczególnej ostrożności i nie może być egzekwowane przez kompilator. Ponadto rozprzestrzenianie plików (Foo.h, FooINLINES.cpp, a teraz także Foo.cpp) jest niepożądane, a dodatkowe jednostki kompilacji komplikują kompilację, która być może była biblioteką tylko nagłówkową.
Czy istnieje lepszy sposób?
Idealnie chciałbym nową do_not_inline_function_calls_inside_me
C++ słowo kluczowe, które mogę używać tak:
do_not_inline_function_calls_inside_me void slow_path1() { ... }
do_not_inline_function_calls_inside_me void slow_path2() { ... }
Alternatywnie inline_function_calls_inside_me
słowo kluczowe, na przykład:
inline_function_calls_inside_me void fast_path() { ... }
Zauważ, że te hipotetyczne słowa kluczowe ozdobić metody *_path*()
, a nie metody.
Przykładowy kontekst, w którym można mieć takie wymagania wydajnościowe, to konkurs programistyczny, w którym każdy uczestnik pisze aplikację, która słucha rozrzuconych globalnych transmisji danych typu A i B. Po otrzymaniu transmisji typu B każda aplikacja musi wykonać obliczenia, które zależą od sekwencji wcześniej nadawanych komunikatów typu-A, i prześlij wynik obliczeń do centralnego serwera. Pierwszy poprawny respondent do każdej transmisji typu B otrzymuje punkt. Natura problemu obliczeniowego może pozwolić na wykonanie precomputation na aktualizacjach typu A; nie ma żadnej korzyści, aby robić to szybko.
Techniki zapobiegające wstawianiu funkcji są specyficzne dla kompilatora. Tak samo jak techniki gwarantujące funkcję. W każdym razie twoje założenie jest błędne i pokazuje mentalność "przedwczesnej optymalizacji". Scenariusz, w którym jedna funkcja jest "krytyczna pod względem wydajności", jest tak rzadki w praktyce, że nie ma sensu. Programiści są znacznie mniej zdolni, niż sądzą, że identyfikują hotspoty wydajności w swoim kodzie - dlatego narzędzia takie jak profilery istnieją. – Peter
@Peter Wydajność krytycznych ścieżek kodowych może być w praktyce rzadkością, ale one istnieją. Jestem szczególnie zainteresowany gcc. – dshin
W gcc możesz podać '__attribute __ ((always_inline))'. –