2012-07-06 16 views
11

Chciałbym wiedzieć, czy istnieje dobry sposób na monitorowanie moich wewnętrznych aplikacji, najlepiej w postaci istniejącej biblioteki.Jak wdrożyć wydajne statystyki środowiska wykonawczego C++

Moja aplikacja jest mocno wielowątkowa i wykorzystuje system przesyłania wiadomości do komunikacji pomiędzy wątkami i światem zewnętrznym. Moim celem jest monitorowanie, jaki rodzaj wiadomości są wysyłane, na jakiej częstotliwości itp.

Mogą być również inne statystyki w bardziej ogólny sposób, np. Ile wątków jest tworzonych co minutę, ile nowych/usuniętych nazwano lub bardziej szczegółowe aspekty aplikacji; Ty to nazwij.

Co byłoby niesamowite to coś takiego jak "wewnętrzne strony" do przeglądarki Google Chrome, takie jak net lub chrome: // tracing, ale w stylu linii poleceń.

Jeśli istnieje biblioteka, która jest na tyle uniwersalna, aby dostosować się do specyfiki mojej aplikacji, byłoby świetnie.
W przeciwnym razie jestem gotowy do wdrożenia małej klasy, która wykona zadanie, ale nie wiem od czego zacząć. Myślę, że najważniejszą rzeczą jest to, że kod nie powinien zbytnio przeszkadzać, aby nie wpłynęły na wyniki.

Czy macie jakieś wskazówki w tej sprawie?

Edit: moja aplikacja działa na Linuksie, w osadzonym środowiska, niestety nie jest obsługiwany przez Valgrind :(

+0

by gprof być obsługiwane? -pg na kompilatorze GCC? – pyCthon

+0

Tak, to jedna rzecz, którą mamy. Chociaż mój problem dotyczyłby programu, który działa przez bardzo długi czas (usługa), więc statystyki powinny być dostępne w czasie wykonywania :-) – Gui13

+1

Szkoda, że ​​nie można dodać kolejnego tagu "osadzonego". –

Odpowiedz

3

Polecam że w kodzie, należy zachować liczniki, które się przyrostowej Liczniki mogą być static członków klasy. lub globalne. Jeśli używasz klasę zdefiniować swój licznik, można mieć konstruktor zarejestrować swój licznik z jednym repozytorium wraz z nazwą. Następnie można wyszukać i zresetować liczniki przez konsultacji z repozytorium.

struct Counter { 
    unsigned long c_; 
    unsigned long operator++() { return ++c_; } 
    operator unsigned long() const { return c_; } 
    void reset() { unsigned long c = c_; ATOMIC_DECREMENT(c_, c); } 
    Counter (std::string name); 
}; 

struct CounterAtomic : public Counter { 
    unsigned long operator++() { return ATOMIC_INCREMENT(c_, 1); } 
    CounterAtomic (std::string name) : Counter(name) {} 
}; 

ATOMIC_INCREMENT byłby mechanizmem zwiększającym skalę licznik atomowy. GCC zapewnia w tym celu wbudowany __sync_add_and_fetch. ATOMIC_DECREMENT jest podobny, z GCC wbudowanym __sync_sub_and_fetch.

struct CounterRepository { 
    typedef std::map<std::string, Counter *> MapType; 
    mutable Mutex lock_; 
    MapType map_; 
    void add (std::string n, Counter &c) { 
     ScopedLock<Mutex> sl(lock_); 
     if (map_.find(n) != map_.end()) throw n; 
     map_[n] = &c; 
    } 
    Counter & get (std::string n) const { 
     ScopedLock<Mutex> sl(lock_); 
     MapType::const_iterator i = map_.find(n); 
     if (i == map_.end()) throw n; 
     return *(i->second); 
    } 
}; 

CounterRepository counterRepository; 

Counter::Counter (std::string name) { 
    counterRepository.add(name, *this); 
} 

Jeśli znasz ten sam licznik będzie zwiększany o więcej niż jednym wątku, a następnie użyć CounterAtomic. W przypadku liczników specyficznych dla wątku wystarczy użyć Counter.

+0

To jest dobry start IMO, lepszy niż sugestia valgrind, ponieważ będzie to naruszać '.. tak, że wydajność nie ma wpływu..' wymaganie. To, co ten adres jest nieważny to wielowątkowy aspekt, zwiększanie/resetowanie liczników niekoniecznie atomowych ... Zastanowiłbym się nad utrzymaniem niektórych z nich w zmiennych prywatnych wątku ... – nhed

+0

@nhed: Dzięki za przypomnienie mi o PO MT wymagania. Zaktualizowałem odpowiedź operacjami atomowymi. – jxh

+0

To wspaniała odpowiedź. Spróbuję tego i zobaczę jak to działa. Dziękuję również za "__sync _ * _ and_fetch", nie wiedziałem o tym i użyłem kilku muteksów! – Gui13

0

Spójrz na valgrind/callgrind.

Może być używany do profilowania, co rozumiem, czego szukasz. Nie sądzę, że działa to w czasie wykonywania, ale może generować po zakończeniu procesu.

+0

@ Gui13: Szukasz statystyk kompilacji, które może dostarczyć walgrind? A może szukasz statystyk czasu wykonania, np. Ile razy użytkownik kliknął wyskakującą kaczkę? –

+0

Niestety, moja aplikacja działa na platformie osadzonej, która nie jest obsługiwana przez valgrind. – Gui13

3

Zdaję sobie sprawę, że próbujesz wdrożyć zbieranie statystyk czasu wykonania - takie rzeczy, jak liczba wysłanych bajtów, czas pracy i liczba aktywacji określonej funkcji przez użytkownika.

Zazwyczaj w celu kompilowania statystyk wykonawczych takich jak te z różnych źródeł (takich jak wątki robocze), chciałbym, aby każde źródło (wątek) zwiększało własne, lokalne liczniki najbardziej podstawowych danych, ale nie wykonywało ich jakakolwiek długa matematyka lub analiza tych danych.

Następnie z powrotem w wątku głównym (lub w dowolnym miejscu, w którym statystyki te mają być analizowane & wyświetlane), wysyłam wiadomość typu RequestProgress do każdego wątku roboczego.W odpowiedzi wątki robocze zbierają wszystkie podstawowe dane i być może przeprowadzają prostą analizę. Te dane, wraz z wynikami analizy podstawowej, są odsyłane z powrotem do wątku wnioskującego (głównego) w komunikacie ProgressReport. Główny wątek agreguje następnie wszystkie te dane, wykonuje dodatkową (prawdopodobnie kosztowną) analizę, formatowanie i wyświetlanie użytkownikowi lub protokołowi.

Główny wątek wysyła tę wiadomość RequestProgress na żądanie użytkownika (np. Po naciśnięciu klawisza S) lub w interwale czasowym. Jeśli chodzi o przedział czasowy, zazwyczaj używam nowego wątku "pulsu". Cały wątek ma numer Sleep() przez określony czas, a następnie wyślij wiadomość Heartbeat do wątku głównego. Główny wątek z kolei działa na ten komunikat Heartbeat wysyłając komunikaty RequestProgress do każdego wątku roboczego, z którego mają być zbierane statystyki.

Akt zbierania statystyk wydaje się być dość prosty. Dlaczego więc taki złożony mechanizm? Odpowiedź jest dwojakie.

Po pierwsze, wątki robocze mają zadanie do wykonania, a obliczanie statystyk użytkowania nie jest to. Próbując naprawić te wątki, aby wziąć na siebie drugą odpowiedzialność, ortoganalia do ich głównego celu jest trochę jak próba zacięcia kwadratowego kołka do okrągłej dziury. Nie zostały zbudowane, aby to zrobić, więc kod będzie odporny na pisanie.

Po drugie, obliczanie statystyk czasu wykonania może być kosztowne, jeśli spróbujesz zrobić zbyt wiele, zbyt często. Załóżmy na przykład, że masz wątek roboczy, który wysyła dane multiemisji w sieci i chcesz zbierać dane o przepustowości. Ile bajtów, ile czasu i średnia liczba bajtów na sekundę. Możliwe, że wątek roboczy wylicza to wszystko w locie, ale jest to dużo pracy i czas procesora jest lepiej wydany przez wątek roboczy, który robi to, co powinien - wysyłając dane multiemisji. Jeśli zamiast tego po prostu zwiększysz licznik o ile bajtów wysłałeś przy każdym wysłaniu wiadomości, to zliczanie ma minimalny wpływ na wydajność wątku. Następnie w odpowiedzi na okazjonalnym RequestProgress wiadomości można dowiedzieć początku & razy zatrzymywać i wysłać tylko, że wraz pozwolić wątek główny zrobi wszystko Division itp

1

korzystają z pamięci współdzielonej (POSIX, System V, lub cokolwiek mmap masz dostępne). Umieść stałą tablicę liczb losowych 32- lub 64-bitowych liczb całkowitych niepodpisanych (tzn. Największej, w której możesz atomowo zwiększać swoją platformę), przesyłając surowy blok pamięci do definicji macierzy. Zwróć uwagę, że lotny nie powoduje atomowości; uniemożliwia optymalizacje kompilatora, które mogą zniszczyć twoje statystyki. Użyj elementów wewnętrznych, takich jak __sync_add_and_fetch() gcc lub nowszych typów atomów C++ 11 atomic <.

Następnie można napisać mały program, który łączy się z tym samym blokiem pamięci współdzielonej i może wydrukować jedną lub wszystkie statystyki. Ten mały program czytający statystyki i program główny będą musiały współdzielić wspólny plik nagłówkowy, który wymusza pozycję każdej statystyki w tablicy.

Oczywistą wadą jest to, że utknąłeś ze stałą liczbą liczników. Ale trudno go pokonać, pod względem wydajności. Uderzenie to przyrost atomowy liczby całkowitej w różnych punktach programu.

1

W systemach wbudowanych powszechną techniką jest zarezerwowanie bloku pamięci dla "dziennika" i potraktowanie go jako okrągłej kolejki. Napisz kod, który może odczytać ten blok pamięci; które pomogą wykonać "migawki" podczas pracy.

Wyszukaj w internecie "rejestrowanie debugowania". Powinien pojawić się źródło, z którego można korzystać. Większość sklepów, w których grałem, zwykle wykonuje własne.

Jeśli masz dodatkową pamięć nieulotną, możesz zarezerwować miejsce i napisać do tego. Obejmuje to również pliki, jeśli twój system jest wystarczająco duży, aby obsługiwać system plików.

W najgorszym przypadku zapisz dane do portu debugowania (portu szeregowego).

przypadku rzeczywistego w czasie rzeczywistym pomiarów, zwykle przy pomocy oscyloskopu połączony z GPIO lub badany punkt i impulsów do punktu/Test GPIO.

0

To dobra odpowiedź, @John Dibling! Miałem system podobny do tego. Jednak mój wątek "stat" wyszukał pracowników 10 razy na sekundę i wpłynął na wydajność wątków roboczych, ponieważ za każdym razem, gdy wątek "stat" prosi o dane, istnieje sekcja krytyczna uzyskująca dostęp do tych danych (liczniki itp.) I oznacza, że ​​wątek roboczy jest blokowany na czas pobierania tych danych. Okazało się, że przy dużym obciążeniu wątków roboczych to 10Hz zapytanie statystyczne wpłynęło na ogólną wydajność pracowników.

Więc przełączyłem się na nieco inny model raportowania statystyk - zamiast aktywnie wysyłać zapytania do wątków roboczych z głównych wątków, mam teraz wątki robocze do zgłaszania swoich podstawowych statystyk statystyk do ich wyłącznych statystyk, które mogą być sprawdzane przez głównego w dowolnym momencie bez bezpośredniego wpływu na pracowników.

0

Jeśli jesteś w C++ 11 można użyć std :: atomową <>

#include <atomic> 

class GlobalStatistics { 
public: 

    static GlobalStatistics &get() { 
     static GlobalStatistics instance; 
     return instance; 
    } 

    void incrTotalBytesProcessed(unsigned int incrBy) { 
     totalBytesProcessed += incrBy; 
    } 

    long long getTotalBytesProcessed() const { return totalBytesProcessed; } 


private: 

    std::atomic_llong totalBytesProcessed; 

    GlobalStatistics() { } 
    GlobalStatistics(const GlobalStatistics &) = delete; 
    void operator=(const GlobalStatistics &) = delete; 
}; 
Powiązane problemy