2012-06-05 12 views
13

Krótki wstęp: Pracuję nad kodem wielowątkowym i muszę udostępniać dynamicznie przydzielane obiekty między dwoma wątkami. Aby uczynić mój kod czystszym (i mniej podatnym na błędy) chcę jawnie "usunąć" obiekty w każdym wątku i dlatego chcę użyć shared_ptr.Narzut i implementacja za pomocą shared_ptr

Pierwsze pytanie:

Chcę wiedzieć, czy realizacja -> operator w shared_ptr ma jakiś dodatkowy narzut (np większy następnie unique_ptr) w czasie wykonywania. Obiekty, o których mówię, to zazwyczaj instancje longlife kopiowane tylko raz po stworzeniu (kiedy rozprowadzam je między wątkami), wtedy uzyskuję dostęp tylko do metod i pól tych obiektów.

Mam świadomość, że shared_ptr chroni tylko liczenie odwołań.

Drugie pytanie:

Jak dobrze zoptymalizowane są shared_ptr w libstdC++? Czy zawsze używa muteksu lub wykorzystuje operacje atomowe (skupiam się na platformach x86 i ARM)?

+3

W dobrej implementacji 'shared_ptr', powinno być zero narzutów podczas dereferencji wskaźnika przez' -> '. Nie jestem zaznajomiony z libstdC++, więc nie mogę odpowiedzieć na twoje drugie pytanie. Masz nagłówki, więc możesz łatwo dowiedzieć się, sprawdzając, w jaki sposób jest zaimplementowany. –

+2

Jeśli kod jest wielowątkowy, współdzielony wskaźnik GCC używa 'std :: atomic ' lub coś podobnego do licznika odwołań; czy to prawdziwy sprzętowy (blokujący) atomowy zależy od wersji kompilatora - uważam, że poprawiono to w GCC 4.7.0. –

+3

Kopiowanie/przypisywanie/wychodzenie z zakresu ma dodatkowy narzut z powodu przyrostu wartości wątków w repozytorium. 'operator->' wygląda dokładnie tak samo, jak starego dobrego 'auto_ptr', tzn. można oczekiwać, że będzie to zero narzutów. – Damon

Odpowiedz

14

Pierwsze pytanie: using operator->

Wszystkie implementacje Widziałem mieć lokalny cache T* prawo w klasie shared_ptr<T> tak, że pole jest na stosie, operator-> ma zatem porównywalne koszt użycia lokalnego stosu T*: w ogóle nie ma narzutu.

Drugie pytanie: mutex/Atomics

Spodziewam libstdC++ używać ATOMiCS na platformie x86, czy za pomocą standardowych urządzeń lub konkretnych intrinsics g ++ (w starszych wersjach). Wierzę, że wdrożenie Boost już to uczyniło.

Nie mogę jednak komentować ARM.

Uwaga: C++ 11 wprowadzające semantykę ruchu, wiele kopii jest naturalnie omijanych przy użyciu shared_ptr.

Uwaga: czytaj o prawidłową eksploatację shared_ptrhere można używać odwołań do shared_ptr (const lub nie), aby uniknąć większości kopii/zniszczenia w ogóle, więc wydajność tych nie jest zbyt ważne.

+0

w odpowiedzi, którą załączyłeś, mówi się, że używasz 'make_shared'. Zastanawiam się, jak użyć tego szablonu na liście inicjalizacji konstruktora? Przykład, klasa 'Foo' ma pole' shared_ptr num', więc konstruktor powinien wyglądać tak: 'Foo :: Foo (void): num (move (make_shared (new int (30))) {...}' ? – Goofy

+2

@Goofy: nie, z 'make_shared' nie jawnie wykonujesz' new', az drugiej strony musisz jawnie przekazać typ utworzonego obiektu; również wezwanie do "przeniesienia" jest niepotrzebne w przypadku tymczasowego. Dlatego daje: 'Foo :: Foo(): num (std :: make_shared (30)) {}' –

+0

Ok, świetnie :) Wciąż przyzwyczajam się do rvalues ​​w C++;) – Goofy

12

GCC's shared_ptr nie użyje blokowania ani atomów w kodzie jednowątkowym. W wielowątkowym kodzie będzie używał operacji atomowych, jeśli atomowa instrukcja porównania i wymiany jest obsługiwana przez procesor, w przeciwnym razie liczniki odwołań są chronione przez muteks. Na i486 i później używa on atomów, i386 nie obsługuje cmpxchg, więc używa implementacji opartej na muteksie. Wierzę, że ARM używa atomów do architektury ARMv7 i później.

(To samo dotyczy zarówno std::shared_ptr, jak i std::tr1::shared_ptr.)

+1

W jaki sposób GCC wie, czy kod jest/będzie wielowątkowy, czy nie? –

+0

@DrewNoakes - musisz powiedzieć to z #define. –

+0

Czy masz odwołanie do tego? Zrobiłem trochę wyszukiwania i nie mogę znaleźć. –

Powiązane problemy