Załóżmy, że istnieją dwa wątki, które komunikują się poprzez asynchroniczne wysyłanie do siebie komunikatów danych. Każdy wątek ma jakąś kolejkę komunikatów.
Moje pytanie jest bardzo niskie: czego można się spodziewać jako najbardziej efektywnego sposobu zarządzania pamięcią? Mogę wymyślić kilka rozwiązań:
- Nadawca tworzy obiekt przez
new
. Połączenia z odbiorcądelete
. - Pula pamięci (aby przenieść pamięć z powrotem do nadawcy)
- Wywóz śmieci (np. Boehm GC)
- (jeśli obiekty są wystarczająco małe) kopiuj według wartości, aby całkowicie uniknąć alokacji sterty
1) jest najbardziej oczywistym rozwiązaniem, więc użyję go jako prototypu. Są szanse, że jest już wystarczająco dobry. Ale niezależnie od mojego konkretnego problemu, zastanawiam się, która technika jest najbardziej obiecująca, jeśli optymalizujesz wydajność.
Spodziewałbym się, że pula teoretycznie jest najlepsza, zwłaszcza że możesz wykorzystać dodatkową wiedzę na temat przepływu informacji między wątkami. Obawiam się jednak, że najtrudniej jest też dobrze się postarać. Dużo tuningu ... :-(
Odśmiecanie powinno być dość łatwe do późniejszego dodania (po rozwiązaniu 1) i spodziewałbym się, że będzie działał bardzo dobrze. Sądzę więc, że jest to najbardziej praktyczne rozwiązanie, jeśli 1) okaże się zbyt nieefektywne.
Jeśli obiekty są małe i proste, kopiowanie według wartości może być najszybsze. Obawiam się jednak, że wymusza to niepotrzebne ograniczenia w implementacji obsługiwanych komunikatów, więc chcę tego uniknąć.
źródło
unique_ptr
, myślę, że masz na myślishared_ptr
. Ale chociaż nie ma wątpliwości, że użycie inteligentnego wskaźnika jest dobre do zarządzania zasobami, nie zmienia to faktu, że używasz jakiejś formy alokacji pamięci i dezalokacji. Myślę, że to pytanie jest bardziej ogólne.Największym osiągnięciem przy przekazywaniu obiektu z jednego wątku do drugiego jest narzut związany z chwytaniem zamka. Jest to rzędu kilku mikrosekund, co oznacza znacznie więcej niż średni czas, jaki zajmuje para
new
/delete
(rzędu stu nanosekund). Rozsądnenew
implementacje starają się unikać blokowania za niemal wszystkimi kosztami, aby uniknąć spadku wydajności.To powiedziawszy, chcesz upewnić się, że nie musisz chwytać zamków podczas komunikowania obiektów z jednego wątku do drugiego. Znam dwie ogólne metody osiągnięcia tego celu. Oba działają tylko w jednym kierunku między jednym nadawcą a jednym odbiorcą:
Użyj bufora dzwonka. Oba procesy kontrolują jeden wskaźnik do tego bufora, jeden jest wskaźnikiem odczytu, drugi jest wskaźnikiem zapisu.
Nadawca najpierw sprawdza, czy jest miejsce na dodanie elementu, porównując wskaźniki, a następnie dodaje element, a następnie zwiększa wskaźnik zapisu.
Odbiornik sprawdza, czy istnieje element do odczytu, porównując wskaźniki, a następnie czyta element, a następnie zwiększa wskaźnik odczytu.
Wskaźniki muszą być atomowe, ponieważ są dzielone między wątkami. Jednak każdy wskaźnik jest modyfikowany tylko przez jeden wątek, drugi potrzebuje tylko dostępu do odczytu wskaźnika. Elementy w buforze mogą być samymi wskaźnikami, co pozwala łatwo dopasować rozmiar bufora pierścieniowego do rozmiaru, który nie spowoduje zablokowania nadawcy.
Użyj połączonej listy, która zawsze zawiera co najmniej jeden element. Odbiorca ma wskaźnik do pierwszego elementu, nadawca ma wskaźnik do ostatniego elementu. Te wskaźniki nie są udostępniane.
Nadawca tworzy nowy węzeł dla listy połączonej, ustawiając jego
next
wskaźnik nanullptr
. Następnie aktualizujenext
wskaźnik ostatniego elementu, aby wskazywał na nowy element. Na koniec zapisuje nowy element we własnym wskaźniku.Odbiornik obserwuje
next
wskaźnik pierwszego elementu, aby sprawdzić, czy są dostępne nowe dane. Jeśli tak, usuwa stary pierwszy element, przesuwa swój własny wskaźnik, aby wskazywał na bieżący element i rozpoczyna przetwarzanie.W tym ustawieniu
next
wskaźniki muszą być atomowe, a nadawca musi upewnić się, że nie odrzuci drugiego ostatniego elementu po ustawieniunext
wskaźnika. Zaletą jest oczywiście to, że nadawca nigdy nie musi blokować.Oba podejścia są znacznie szybsze niż jakiekolwiek podejście oparte na blokadzie, ale wymagają starannej implementacji, aby uzyskać prawidłowe działanie. I oczywiście wymagają natywnej atomowości sprzętowej zapisów / obciążeń wskaźników; jeśli twoja
atomic<>
implementacja używa blokady wewnętrznie, jesteś prawie skazany.Podobnie, jeśli masz kilku czytelników i / lub pisarzy, jesteś prawie skazany: możesz spróbować wymyślić schemat bez blokady, ale wdrożenie go będzie trudne. Sytuacje te są znacznie łatwiejsze w obsłudze za pomocą zamka. Jednak, gdy chwycić blokadę można przestać się martwić o
new
/delete
wydajności.źródło