2012-03-09 8 views
22

W this pytanie Howard Hinnant powiedziałDlaczego nie jest dobrze używać dziedziczenia cyklicznego dla implementacji std :: tuple?

Niektóre implementacje std :: krotki użyć rekurencyjnej dziedziczenia. Ale ci dobrzy nie. ;-)

Czy ktoś może rzucić trochę światła na to?

+1

może ma to związek z nieefektywnym przekazywaniem argumentów? –

+0

byłby prawdopodobnie pomocny, gdyby ktoś mógł przedstawić różnicę między tymi dobrymi a tymi, które są dobre. – PlasmaHH

+0

@Cheers: Sądzę, że inlining poradziłby sobie z tym - ale z drugiej strony muszę przyznać, że * mam * uruchamiane kompilatory, które zdecydowały się zaprzestać wprowadzania na głębokość większą niż 3 lub 4. – Hurkyl

Odpowiedz

29

A non-recursive implementation ma lepszą wydajność podczas kompilacji. Wierzcie lub nie, w mocno wykorzystywanym obiekcie bibliotecznym, takim jak std::tuple, sposób implementacji może mieć wpływ (na lepsze lub gorsze) na czasy kompilacji, jakie widzi klient. Rekurencyjne implementacje mają tendencję do tworzenia czasów kompilacji, które są liniowe w zakresie rekursji (lub mogą być nawet gorsze).

Wpływa to nie tylko na powstawanie samej krotki. std::get<I>(tuple) na przykład zajmie liniową ilość czasu kompilacji dla jednej implementacji i stałą ilość czasu kompilacji dla innej implementacji. Ten wpływ może szybko ulec pogorszeniu (lub nie) w przypadku krotek krotek. To znaczy. rekurencyjna implementacja może spowodować kompilację O (N^2), podczas gdy implementacja nierekurencyjna nadal będzie O (1).

Fwiw, implementacja libC++ kładzie obiekty w kolejności określonej przez klienta, ale optymalizuje przestrzeń dla pustych komponentów przy użyciu pustego obiektu optymalizacji klasy bazowej kompilatora.

+2

To pytanie (i Twoja odpowiedź) skłoniło mnie do zbadania nierekurencyjnej implementacji krotki. Napisałem o tym post: http://mitchnull.blogspot.com/2012/06/c11-tuple-implementation-details-part-1.html. Byłbym wdzięczny, gdybyś mógł poświęcić trochę czasu na przeczytanie i wskazanie wszelkich błędów. Dzięki – mitchnull

+1

Użyłem implementacji Howards do ulepszenia mniejszego programu. Włączyłem również [triki indeksowe] (http://stackoverflow.com/a/20045842/2712726) w testach poprawiających element tuple_element pod względem czasu kompilacji i rozmiaru pliku wykonywalnego. Ku mojemu zaskoczeniu rzeczy nie były lepsze, ale gorsze. Używam gcc 4.8.2. Czy najnowszy gcc to coś, co zoptymalizowało rekursję szablonów ogona? A może wspomniane sztuczki działają tylko w przypadku narożników? Czy istnieje przykład lub historia sukcesu potwierdzająca skuteczność optymalizacji? –

3

Nie przypominam sobie przemówienia GoingNative 2012 Andrei Alexandrescu dokładnie, ale mówił o tym punkcie, a jednym z punktów, o których wspomniał był układ pamięci. Jeśli mam std::tuple<int, short, char, char>, będzie on w pamięci jako char, short, int, a ten układ zajmie (w moim systemie) 4 bajty więcej, niż gdyby zostały one ustawione jako int, short, char. R. Martinho Fernandes przypomniała mi, że najlepszą rzeczą do zrobienia byłoby zamówienie ich w pamięci w kolejności, która minimalizuje dopełnienie, co nie jest ani porządkiem w kolejności ani kolejnością odwrotną. (Dziedziczenie naiwne ma kolejność odwrotną).

Jeśli piszę std::tuple<int, char, short, char>, krotka, która działa przez naiwnego dziedziczenia byłoby umieścić je w kolejności char, short, int w pamięci za pomocą 3 bajtów wypełnienia, gdy optymalna wynosi zero bajtów wypełnienia. (Albo int, short, char, char lub char, char, short, int).

Zakładając, że mam rację, że chodzi o dopełnienie, to R. Martinho Fernandes said "[moja argumentacja] nie wyklucza użycia rekurencyjnego dziedziczenia dla rzeczywistej implementacji w optymalnej kolejności.", Dlatego właśnie określić, że dziedziczenie naiwne jest złe.

(Kolejność w pamięci robi nie oznacza, że ​​get<0> da inny obiekt, a R. Martinho Fernandes słusznie zauważa, że ​​kolejność powinna być niewidoczne dla użytkownika. Jednak były to punkty jak już przypomniano od zdarzenia GoingNative.)

Film jest dostępny pod numerem http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Variadic-Templates-are-Funadic, a slajdy mają numer http://ecn.channel9.msdn.com/events/GoingNative12/GN12VariadicTemplatesAreFunadic.pdf.

+0

Powinieneś powiedzieć co rozmowa. –

+0

Gdzie on mówi o czymkolwiek innym w swoim komentarzu? –

+0

Z jakiegoś powodu myślałem, że Johannes mówił o GoingNative, nie wiem dlaczego tak pomyślałem. –

2

Jednym z powodów, dla których nie należy używać łańcucha klas podstawowych, jest brak łańcucha konstruktorów: argumenty są przekazywane bezpośrednio do odpowiedniego obiektu podrzędnego. Wydaje się również, że implementacja nierekurencyjna powoduje dużo mniejsze obciążenie kompilatora i tworzy o wiele mniej [wewnętrznych] symboli. Nie wspominając o tym, że tak naprawdę jest to łatwiejsze do przeniesienia do klasy bazowej.

Powiązane problemy