Kiedy inline powinno być używane w Rust?

86

Rust ma atrybut „inline”, którego można użyć w jednym z tych trzech rodzajów:

#[inline]

#[inline(always)]

#[inline(never)]

Kiedy należy ich używać?

W odwołaniu do Rusta widzimy sekcję atrybutów inline mówiącą

Kompilator automatycznie wstawia funkcje na podstawie wewnętrznej heurystyki. Nieprawidłowe wstawianie funkcji może w rzeczywistości spowolnić program, dlatego należy z niego korzystać ostrożnie.

Na forum wewnętrznym Rusta huon również konserwatywnie podchodził do specyfikacji inline .

Ale widzimy znaczne użycie w źródłach Rusta, w tym w bibliotece standardowej. Wiele atrybutów wbudowanych jest dodawanych do funkcji jednowierszowych, co powinno być łatwe dla kompilatorów do wykrycia i optymalizacji poprzez heurystykę zgodnie z odniesieniem. Czy w rzeczywistości nie są one potrzebne?

WiSaGaN
źródło

Odpowiedzi:

70

Jednym z ograniczeń obecnego kompilatora Rusta jest to, że jeśli nie używasz LTO (optymalizacji czasu łącza), nigdy nie wstawi funkcji nieoznaczonej #[inline]w skrzynkach. Rust używa oddzielnego modelu kompilacji podobnego do C ++, ponieważ implementacja LTO LLVM nie jest dobrze skalowana do dużych projektów. Dlatego małe funkcje wystawione na inne skrzynie należy oznaczać ręcznie. To nie jest świetna sytuacja i prawdopodobnie zostanie naprawiona w przyszłości dzięki pewnej kombinacji ulepszeń wbudowanych LTO i MIR.

#[inline(never)]jest czasami przydatny do debugowania (oddzielania fragmentu kodu, który nie działa zgodnie z oczekiwaniami). Teoretycznie można go używać do testów porównawczych, ale zwykle jest to zły pomysł: wyłączenie inliningu nie zapobiega innym optymalizacjom międzyprocedurowym, takim jak ciągła propagacja. Jeśli chodzi o normalny kod, może to zmniejszyć rozmiar kodu, jeśli masz często używaną funkcję pomocniczą, która jest używana tylko do obsługi błędów.

#[inline(always)]to ogólnie zły pomysł; jeśli funkcja jest na tyle duża, że ​​kompilator nie będzie jej domyślnie wbudowywana, jest na tyle duża, że ​​narzut wywołania nie ma znaczenia (a nadmierne wstawianie zwiększa ciśnienie w pamięci podręcznej instrukcji). Są wyjątki, ale aby to uzasadnić, potrzebne są pomiary wydajności. Ten przykład to sytuacja, w której warto się zastanowić. #[inline(always)]można również użyć do poprawy -O0jakości kodu, ale zwykle nie warto się tym martwić.

Eli Friedman
źródło
19
należy zauważyć, że inline(never)jest on używany w wewnętrznych elementach paniki, aby upewnić się, że optymalizator nie wbudowuje funkcji, które są wywoływane tylko w przypadku paniki.
oli_obk
4
-1, ponieważ czegoś brakuje w pierwszym punkcie. Elementy ogólne można wstawiać w skrzynkach, ponieważ są one efektywnie kompilowane podczas tworzenia instancji, więc kod potrzebny do wstawiania jest łatwo dostępny. A to oznacza, że ​​ogromna liczba takich elementów, które nie są oznaczone, nadal może być umieszczona w skrzynce krzyżowej. W niektórych typach podstawowych skrzynek bibliotek każdy element jest ogólny!
bluss