2011-12-09 11 views
7

mam metodę DLL, który powinien być „QoSed” - metoda ta powinna być nazywana maksymalną 100 razy na sekundę .:najlepszym sposobem, aby policzyć, ile razy w ciągu sekundy metoda nazywa

private static extern int ExecTrans(int connectionId); 

Metoda ta używana tylko w jednym miejscu w programie, więc jest ok do tego miejsca. Potrzebuję osobnego "licznika qos" dla każdego connectionId. Tak więc ExecTrans(1) i ExecTrans(2) powinny iść do różnych liczników.

Na początku mojej iteracji chciałbym policzyć jak często metoda jest wywoływana (dla każdego connectionId). To znaczy. Chcę mieć "statystyki na żywo". Istnieją dwa podejścia:

- allow to exceed limitiation for a short period. for example allow "100 transaction from 0 to 1 second, 100 transaction from 1 to 2 seconds and 200 transactions from 0.5 to 1.5 second". 
- at any second interval transactions should not exceed 100. 

Na razie nie obchodzi mnie, która z tych metod do wykorzystania, ale chciałbym wybrać jedną tworząc mniej „narzut”. Chcę qos dodać jako mniej "dodatkową pracę", ponieważ jest to oprogramowanie handlowe wrażliwe na każde 0,1 ms.

Jak za pierwszym podejściu myślę, że mogę używać coś takiego (pseude kodu, prawdopodobnie stats i curStats należy thread-safe):

private int[] stats  // statistic to display to user 
private int[] curStats; // statistic currently collection 

OnOneSecondElapsed(object source, ElapsedEventArgs args) { 
    foreach (conId : connIds) { 
     stats[conId] = curStats[conId]; 
     curStats[conId] = 0; 
    } 
} 

myMethod { 
    ...... 
    ExecTrans(conId); 
    ++curStats[conId]; 
    ...... 
} 

Co do drugiego podejścia jest to .... czy można stworzyć kolekcję, w której przedmioty żyją dokładnie przez jedną sekundę, a po chwili znikają? Za każdym razem dodam następny obiekt do kolekcji, chyba że kolekcja zawiera 100 obiektów.

Co myślisz? Nie jestem zaznajomiony z plikami biblioteki C#, więc prawdopodobnie brakuje mi niektórych przydatnych klas, prawdopodobnie możesz zaproponować inne podejście.

Odpowiedz

5

Pierwsze podejście:

  • Użyj ConcurrentQueue<DateTime>
  • Przed każdą prośbę, sprawdzić rozmiar kolejki. Jeśli> 100, anulować żądanie
  • jeśli < 100, enqueue bieżącego DateTime i wykonania wniosku
  • w wątku tła, na co 0,1 sekundy, usunąć wpisy starsze niż 1 sekunda

To powinno być dość wydajny, ale:

  • Ponieważ nie ma zamek między czasie sprawdzić liczbę kolejek i czas, który enqueue, można czasami dostać nieco ponad 100 żądań na sekundę
  • sinc e wątek tła wykonuje się co 0,1 sekundy, jeśli otrzymasz 100 żądań w tym samym czasie, może zablokować kolejkę nawet do 1,1 sekundy. Dostosuj czas snu w razie potrzeby.

Mogę się mylić, ale nie sądzę, że istnieje idealne rozwiązanie. Zasadniczo, im dokładniejszy jest system, tym większy jest narzut.Musisz dostosować parametry w zależności od potrzeb.

+0

Sądzę, że istnieje pewne zamieszanie związane z tym, co należy zrobić po przekroczeniu limitu. Upuść nowe elementy pracy? Upuść stare elementy pracy (jeśli nie występują w sieci)? Wprowadzić przerwę? –

+0

wprowadzenie pauzy – javapowered

+0

Następnie może dodać spinlock, gdy liczba jest wyższa niż 100, aby ponownie sprawdzić. –

3

Istnieje narzędzie o nazwie profiler, które robi dokładnie to, czego szukasz. Uruchomisz go za pomocą swojego kodu, który dokładnie określi, ile czasu spędził w każdej metodzie i ile razy każda metoda została wywołana. Tutaj jest old thread o profilerach C#. Jeśli jesteś profesjonalnym programistą, być może masz już licencję firmową na profilera.

+2

Potrzebuję QOS w przyszłości! Muszę wprowadzić opóźnienia, jeśli jakaś metoda nazywała się większą niż wtedy pewną wartością. I nie chcę używać profilier, ponieważ jest zbyt wolny. Profilier jest dobry do debugowania, ale muszę mieć tę funkcję w produkcji. – javapowered

1

W niektórych przypadkach zostaniesz wezwany częściej niż n razy na sekundę, zakładam, że po prostu nie chcesz wykonywać żadnego rzeczywistego przetwarzania w dodatkowych przypadkach.

Możesz użyć synchronizowanego obiektu Q do przechowywania transakcji, które mają być przetwarzane dla każdego połączenia. Wywołanie metody spowoduje tylko dodanie do danych danych, co należy zrobić. W oddzielnym wątku przetwarzania (albo tylko jeden dla systemu lub dla połączenia) można wycofać operacje i przetworzyć je z prędkością 1 na 0,01 sekundy. Po prostu skróć rozmiar Q do 100 (zejdź do 100) przed zarejestrowaniem każdej jednostki pracy dla danego połączenia i voila, odrzuć dodatkowe elementy pracy.

Uwaga: Do wymuszenia 1 transakcji na 0,01 sekundy potrzebna jest dokładna funkcja pomiaru czasu. np

Stopwatch watch = new Stopwatch(); 
int nextPause = watch.Elapsed.Milliseconds + 10; 
while (true) 
{ 
    //do work (dequeue one item and process it) 

    int now = watch.Elapsed.Milliseconds; 
    if(now < nextPause) { 
     Thread.Sleep(nextPause - now); 
    } 
    nextPause = watch.Elapsed.Milliseconds + 10; 
} 

Uwaga: Jeśli transakcja trwa dłużej niż 10 milisekund (1/100th sekundy), można upuścić dodatkowych elementów roboczych ...

Jeśli chcesz nitka pracownik będzie więcej "bursty" można przetworzyć wiele elementów pracy w jednej pętli i użyć dłuższego czasu oczekiwania, co wymagałoby częściowego oczekiwania z częściową liczbą "elementów pozostałych". (również lepiej byłoby użyć Monitora.Pulse i Montior.Wait zamiast spać ...)

0

W przypadku, gdy ktoś musi mierzyć zamiast dławić ... to naiwne podejście, które daje przybliżoną ocenę :

class A{ 
    private int _calls; 
    private Stopwatch _sw; 

    public A(){ 
     _calls = 0; 
     _sw = new StopWatch(); 
     _sw.Start(); 
    } 

    public void MethodToMeasure(){ 
     //Do stuff 
     _calls++; 
     if(sw.ElapsedMilliseconds > 1000){ 
      _sw.Stop(); 
      //Save or print _calls here before it's zeroed 
      _calls = 0; 
      _sw.Restart(); 
     } 
    } 
} 
Powiązane problemy