2012-03-20 21 views
7

Pisałem asynchroniczny framework rejestrowania, w którym miałem wiele wątków wysypujących dane. Zacząłem grać w Boost asio, ponieważ oferował kilka prostych sposobów na wymuszanie serializacji i zamawiania. Ponieważ jestem początkującym, zacząłem mój projekt z bezpiecznym wątku (używany boost::mutex i) circular bounded_buffer (który był w rzeczywistości wektor).Zwiększenie wdrożenia ASIO IO_SERVICE?

Napisałem mały prosty test porównawczy do pomiaru wydajności. Benchmark to tylko jeden wątek rejestrujący milion wiadomości (przesuwając go do bufora), a mój wątek roboczy pobierałby wiadomości z kolejki, aby logować się do pliku/konsoli/listy rejestratorów. (P.S. Używanie muteksa i C.V było poprawne, a wskaźniki do wiadomości były przenoszone, więc z tego punktu widzenia wszystko było w porządku/wydajne).

Kiedy zmieniłem realizację zamiast korzystania boost::asio::io_service i i mających pojedynczy wątek wykonujący run() wydajność naprawdę poprawie (w rzeczywistości jest skalowany naprawdę dobrze na zwiększenie liczby wiadomości są rejestrowane w przeciwieństwie do wydajności poniżające w moim początkowym prostego modelu)

Oto kilka pytań, które chcę wyjaśnić.

  1. Dlaczego poprawa wydajności? (Myślałem, że implementacja wewnętrzna ma wątkową bezpieczną kolejkę dla procedur obsługi, co czyni ją bardziej wydajną niż mój własny początkowy prosty wątek bezpieczny projekt kolejki). Proszę wziąć pod uwagę, że mój projekt został dobrze oceniony i nie miał żadnych wad jako takich (kod szkieletu był oparty na sprawdzonych przykładach), czy ktoś mógłby rzucić więcej światła na wewnętrzne szczegóły tego, jak implementuje to io_service.

  2. Drugą interesującą obserwacją było to, że przy zwiększaniu ilości wątków moja początkowa wydajność implementacji poprawiła się, ale kosztem utraty serializacji/porządku, ale wydajność uległa pogorszeniu (bardzo nieznacznie) dzięki boost :: asio (myślę, że to dlatego, że moje programy obsługi wykonywali bardzo uproszczone zadanie, a przełączanie kontekstu było poniżające, spróbuję wykonać bardziej złożone zadanie i opublikuję swoje obserwacje później).

  3. Naprawdę chciałbym wiedzieć, czy boost::asio jest właśnie przeznaczona dla operacji I/O i sieciowych lub jest mój zwyczaj używania go za to zadanie współbieżne (równoległe) przez puli wątków jest dobre podejście do projektowania. Czy obiekt jest przeznaczony wyłącznie do obiektów i/o (jak napisano w dokumentacji), ale okazało się, że jest to naprawdę ciekawy sposób pomagania mi w rozwiązywaniu równoległych zadań (nie tylko i/o lub związanych z siecią) w serializowany sposób (czasami wymuszając zamawianie za pomocą pasm). Jestem nowy, aby wzmocnić i naprawdę ciekawi, dlaczego podstawowy model nie działał/skalował się, jak również kiedy użyłem boost asio.

Wyniki: (zarówno po prostu miał 1 wątku roboczego)

  • 1000 zadanie: 10 mikro s/zadania w obu przypadkach
  • 10000 zadanie: 80 mikro s (ograniczony bufor), 10 mikro sw impuls asio
  • 100000 zadanie 250 mikro s (bufor bounde), 10 s w mikro impuls asio

byłoby interesujące k teraz jak boost rozwiązuje problem bezpieczny dla wątków w io_service kolejce bezpiecznej dla wątków dla handlerów (zawsze myślałem, że na pewnym poziomie implementacji muszą również używać blokad i c.v).

+0

Użyłem 'boost :: asio' do zarządzania innymi typami zadań asynchronicznych (nie tylko siecią IO), aby osiągnąć sukces. – Chad

+0

Wiem, że jest to stare pytanie, ale może się zdarzyć, że (w systemie Windows) ASIO używa portów zakończenia IO, co może skutkować mniejszą liczbą wywołań systemowych. – Pete

Odpowiedz

4

Obawiam się, że nie może pomóc dużo z (1), ale w odniesieniu do pozostałych dwóch pytań:

(2) I odkryli, że istnieją pewne koszty ogólne w architekturze boost::asio które nie są -deterministyczne, tzn. że opóźnienia między danymi przychodzącymi (lub wysyłanymi do obiektu usługi we/wy) mogą różnić się od praktycznie natychmiastowej odpowiedzi aż do rzędu setek milisekund. Próbowałem to skwantyfikować jako część innego problemu, który próbowałem rozwiązać w odniesieniu do rejestrowania i sygnatury czasowej danych RS232, ale nie uzyskałem żadnych rozstrzygających wyników ani sposobów na ustabilizowanie opóźnień. Nie zdziwiłbym się, gdyby istniały podobne problemy z komponentem zmiany kontekstu.

(3) Jeśli chodzi o używanie boost::asio do zadań innych niż asynchroniczne operacje we/wy, jest to obecnie moje standardowe narzędzie dla większości operacji asynchronicznych. Cały czas używam timerów boost::asio dla procesów asynchronicznych i do generowania limitu czasu dla innych zadań. Możliwość dodawania wielu wątków roboczych do puli oznacza, że ​​możesz skalować rozwiązanie dobrze również dla innych asynchronicznych zadań o dużym obciążeniu. Moja najprostsza i ulubiona klasa pisałem w ostatnim roku jest malutkim klasa Wątek roboczy dla boost::asio usług IO (przepraszam jeśli są jakieś literówki, to transkrypcja z pamięci, a nie cięcie & wklej):

class AsioWorker 
{ 
public: 
    AsioWorker(boost::asio::io_service * service): 
    m_ioService(service), m_terminate(false), m_serviceThread(NULL) 
    { 
    m_serviceThread = new boost::thread(boost::bind(&AsioWorker::Run, this)) 
    } 
    void Run(void) 
    { 
    while(!m_terminate) 
     m_ioService->poll_one(); 
     mySleep(5); // My own macro for cross-platform millisecond sleep 
    } 
    ~AsioWorker(void) 
    { 
    m_terminate = true; 
    m_serviceThread->join(); 
    } 
private: 
    bool m_terminate; 
    boost::asio::io_service *m_ioService; 
    boost::thread *m_serviceThread; 
} 

Ta klasa to świetna zabawka, po prostu dodaj po new w razie potrzeby, a delete trochę, gdy skończysz. Przyklej std::vector<AsioWorker*> m_workerPool do klasy urządzeń, która używa boost::asio i możesz owijać jeszcze bardziej rzeczy związane z zarządzaniem pulami wątków. Zawsze miałem ochotę napisać inteligentny automatyczny menedżer puli na podstawie czasu, aby odpowiednio zwiększyć pulę wątków, ale nie miałem projektu, który byłby jeszcze potrzebny.

Jeśli chodzi o zaspokojenie swojej ciekawości w kwestii bezpieczeństwa nici, możliwe jest wbicie się w odwagę wzmocnienia, aby dowiedzieć się dokładnie, jak robią to, co robią. Osobiście zawsze brałem większość ulepszeń w wartości nominalnej i zakładałem z wcześniejszego doświadczenia, że ​​jest całkiem dobrze zoptymalizowany pod maską.

0

Znalazłem również boost::asio jako doskonałą infrastrukturę dla ogólnego wielordzeniowego silnika przetwarzania. Zmierzyłem jego wydajność w precyzyjnym zadaniu z dużą ilością synchronizacji i odkryłem, że przewyższa ona "klasyczną" implementację, którą napisałem przy użyciu wątków C++ 11 i zmiennych warunkowych.

Również przewyższa TBB, ale nie o tyle. Wkopałem się w ich kod, aby znaleźć "sekret". Jedyne, co widzę, to to, że ich kolejka jest klasyczną połączoną listą, a nie kontenerem STL.

Mimo wszystko, nie jestem pewien, jak dobrze asio byłby skalowalny na masywnej architekturze, takiej jak Xeon Phi. Dwie rzeczy, które wydają się nie być to:

  1. kolejki priorytetowe i
  2. dzieło kolejka kradzież.

Podejrzewam, że dodanie tych funkcji spowodowałoby obniżenie poziomu wydajności TBB.

Powiązane problemy