2017-02-07 3 views
4

Ich habe eine ereignisgesteuerte Anwendung. Ich möchte die Event-Handler (EventHandler Klasse für viele/alle Ereignisse) eine gemeinsame Implementierung zu halten - während die EventSource änderbar sein kann (speziell - zur Kompilierzeit).Callback mit niedriger Latenz in C++

Zur Kopplung der EventHandler mit dem EventSource, werde ich eine Instanz von Handler im EventSource speichern müssen. Ich habe versucht, Handler verschiedene Formen zu speichern:

  • Zeiger auf eine Schnittstelle von EventHandler (die öffentlichen Handler Methoden definierten in Beton EventHandler ‚s
  • Instanz std::function hat - diese bereitgestellt größte Flexibilität

In beiden Fällen war die Latenz beim Aufruf der Zielmethode/Lambda ziemlich hoch (bei meinem Testaufbau ca. 250ns) - und schlimmer noch, war inkonsistent. Kann durch virtuelle Tabellen- und/oder Heap-Zuweisung und/oder sein Typ löschen ???

Um diese Latenz zu reduzieren, möchte ich Vorlagen verwenden.

Das Beste, was ich tun konnte, ist:

template <typename EventHandler> 
class EventSource1 
{ 
    EventHandler* mHandler; 
public: 
    typedef EventHandler EventHandlerType; 

    void AssignHandler (EventHandler* handler) 
    { 
     this->mHandler = handler; 
    } 

    void EventuallyDoCallback (int arbArg) 
    { 
     this->mHandler->CallbackFunction (arbArg); 
    } 
}; 

template <EventSourceType> 
class EventSourceTraits 
{ 
    typedef EventSourceType::EventHandlerType EventHandlerType; 
    static void AssignHandler (EventSourceType& source, EventHandlerType* handler) 
    { 
     source.AssignHandler(handler); 
    } 
}; 

class EventHandler 
{ 
public: 
    void CallbackFunction (int arg) 
    { 
     std::cout << "My callback called\n"; 
    } 
}; 


int main() 
{ 
    EventSource1<EventHandler> source;     /// as one can notice, EventSource's need not to know the event handler objects. 
    EventHandler handler; 

    EventSourceTraits<EventSource1>::AssignHandler (source, &handler); 
} 

Dieses Verfahren eine Beschränkung, dass alle meine EventSource ist ein Template-Klassen sein.

Frage ist: Ist dies der beste Weg, um konsistente und niedrige Latenz zu erreichen Callback? Kann dieser Code verbessert werden, um zu vermeiden, dass die Ereignisquellklassen völlig unabhängig vom Typ der Ereignishandlerobjekte sind?

+1

Haben Sie ausprobiert, was die Latenz ist, wenn Sie - nur zu Diagnosezwecken - einen bekannten Event-Handler von einer bekannten Ereignisquelle über eine statisch gebundene Funktion aufrufen? Nur um herauszufinden, ob die Latenz tatsächlich durch den Aufruf ausgelöst wird und keine anderen Effekte zutreffen (wie zum Beispiel Objekte, die instanziiert werden sollen, Dispatcher-Code zum Auswerten, Threads, die sich gegenseitig sperren, oder was auch immer)? –

+3

Versuchen Sie zunächst, einen unverschmutzten einfachen Zeiger auf eine reguläre Nichtmitgliedsfunktion zu messen. Kann das nicht übertreffen. Wenn Sie die Leistung akzeptabel finden, haben Sie eine Chance. Ein virtueller Aufruf sollte jedoch im Wesentlichen die gleiche Leistung wie ein Funktionszeiger haben. Hast du wirklich Profiling gemacht? –

+0

@StephanLechner, eine wohlbekannte Elementfunktion eines bekannten Objekts benötigt <30ns konsistent. Ein virtueller Anruf dauert ungefähr 60ns, bleibt aber konsistent. Dies ist entweder akzeptabel (weil sie konsistent sind). Ich bekomme ein ähnliches Verhalten mit dem oben genannten Code mit Vorlage. p.s. Ich habe kein Profiling gemacht. – rat6

Antwort

1

Ist dies der beste Weg, konsistente und niedrige Latenz zu Callback zu erreichen?

In Bezug auf die Frage in den Kommentaren vorgeschlagen, würde ich versuchen eher und messen wenn das ist wirklich ein Problem, zu wissen, und was ist die beste Alternative für Sie.
Es gibt keine der beste Weg, es hängt meist auf das eigentliche Problem.

Kann dieser Code verbessert werden, um zu vermeiden, dass die Ereignisquellklassen völlig unabhängig vom Typ der Ereignisbehandlungsobjekte sind?

Vielleicht folgende kann ein guter Punkt, von dem beginnen, das zu erreichen:

#include <iostream> 

class EventSource1 
{ 
    using invoke_t = void(*)(void *C, int value); 

    template<typename T, void(T::*M)(int)> 
    static void proto(void *C, int value) { 
     (static_cast<T*>(C)->*M)(value); 
    } 

    invoke_t invoke; 
    void *handler; 

public: 
    template<typename T, void(T::*M)(int) = &T::CallbackFunction> 
    void AssignHandler (T* ref) 
    { 
     invoke = &proto<T, M>; 
     handler = ref; 
    } 

    void EventuallyDoCallback (int arg) 
    { 
     invoke(handler, arg); 
    } 
}; 

class EventHandler 
{ 
public: 
    void CallbackFunction (int arg) 
    { 
     std::cout << "My callback called: " << arg << std::endl; 
    } 
}; 

int main() 
{ 
    EventSource1 source; 
    EventHandler handler; 

    source.AssignHandler(&handler); 
    source.EventuallyDoCallback(42); 
} 

es Siehe auf wandbox.

+1

Für das Low-Latency-Programm, das ich schreibe, erwartete ich eine Lösung, die mit der Latenz der virtuellen Funktion vergleichbar ist. Ihre Lösung mit Vorlagen funktioniert gut für mich. Danke ! – rat6

+0

@ rat6 Gern geschehen. ;-) – skypjack

Verwandte Themen