2017-02-07 11 views
4

У меня есть приложение, управляемое событиями. Я хочу сохранить обработчик события (EventHandler класс, способный ко многим/всем событиям), общую реализацию - при этом можно изменить переменную EventSource (в частности - во время компиляции).Обратный вызов с низкой задержкой в ​​C++

Для соедини EventHandler с EventSource, я должен хранить экземпляр обработчика в EventSource. Я пытался хранить обработчиков различных форм:

  • указатель на интерфейс EventHandler (то есть публичные методы обработчиков, определенные в конкретном EventHandler «ы
  • экземпляр std::function - это обеспечило большую гибкость

Однако в обоих случаях задержка при вызове целевого метода/лямбда была довольно высокой (на моей тестовой установке около 250 нс) - и, что еще хуже, была непоследовательной. Может быть, из-за размещения виртуальной таблицы и/или кучи и/или стирание типа

Чтобы уменьшить эту задержку, я хочу использовать шаблоны.

Лучшее, что я мог придумать это:

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); 
} 

Этот метод наложить ограничение, что все мои EventSource «s, чтобы быть шаблоном класса.

Вопрос: Это лучший способ добиться последовательной и низкой задержки для обратного вызова? Можно ли улучшить этот код, чтобы избежать того, чтобы исходные классы событий полностью не зависели от типа объектов обработчика событий?

+1

Вы пробовали, что такое латентность, если - только для диагностической цели - вы называете одного хорошо известного обработчика событий из одного хорошо известного источника событий через статически связанную функцию? Просто, чтобы узнать, действительно ли вызвана латентность вызовом, и какие-либо другие эффекты не применяются (например, объекты, которые должны быть созданы, код диспетчера для оценки, потоки, которые блокируют друг друга или что-то еще)? –

+3

Сначала попытайтесь измерить простой неприкрашенный указатель на регулярную функцию, отличную от члена. Не может превзойти это. Если вы считаете, что его эффективность приемлема, у вас есть шанс на бой. Виртуальный вызов должен иметь, по существу, ту же производительность, что и указатель на функцию. Прошли ли вы какие-либо реальные профилирования? –

+0

@StephanLechner, хорошо известная функция-член хорошо известного объекта принимает <30ns последовательно. Виртуальный вызов занимает примерно 60 нс, но остается неизменным. Любое из этого является приемлемым (поскольку они являются согласованными). Я получаю подобное поведение с вышеуказанным кодом с шаблоном. p.s. Я не делал никакого профилирования. – rat6

ответ

1

Это лучший способ добиться последовательной и низкой задержки для обратного вызова?

Как указывается в комментариях к этому вопросу, я бы предпочел попробовать и измерить знать, если это действительно проблема, и что это лучший вариант для вас.
Не существует наилучшим образом, это в основном зависит от фактической проблемы.

Этот код может быть улучшен, чтобы избежать того, чтобы классы источников событий были полностью независимы от типа объектов обработчика событий?

Может быть, следующая может быть хорошей отправной точкой, с которой начинается для достижения этой цели:

#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); 
} 

Посмотрите на wandbox.

+1

Для программы с низкой задержкой, которую я пишу, я ожидал решения, сравнимого с латентностью виртуальной функции. Ваше решение с шаблонами отлично работает для меня. Спасибо ! – rat6

+0

@ rat6 Добро пожаловать. ;-) – skypjack

 Смежные вопросы

  • Нет связанных вопросов^_^