|
Boost Users : |
Subject: Re: [Boost-users] [signals2][review] The review of the signals2 library (formerly thread_safe_signals) begins today, Nov 1st
From: Hansi (hansipet_at_[hidden])
Date: 2008-11-19 16:59:58
Hi,
I don't have a review...
I have made a few tests with the library. I think the library is good,
but I don't know if I make something wrong, but it seems for me that the
library is slow. I have made a few tests with VC++ 8.0 and _SCL_SECURE=0
and it seems that it is about 50 times slower than a normal
boost::function with a bind.... It's a lot...
Best regards
Hansjörg
The test code was:
class PerformanceCounter
{
private:
LARGE_INTEGER CurrentTickCount_;
LARGE_INTEGER OldTickCount_;
LARGE_INTEGER TickFrquency_;
double EllapsedTime_;
double TotalTime_;
public:
PerformanceCounter():
EllapsedTime_(0.0),
TotalTime_(0.0)
{
::QueryPerformanceFrequency(&TickFrquency_);
::QueryPerformanceCounter(&CurrentTickCount_);
OldTickCount_ = CurrentTickCount_;
}
void Reset(const double intial_time = 0.0)
{
TotalTime_ = intial_time;
EllapsedTime_ = intial_time;
::QueryPerformanceCounter(&CurrentTickCount_);
OldTickCount_ = CurrentTickCount_;
}
void CalculateEllapsedTime()
{
::QueryPerformanceCounter(&CurrentTickCount_);
EllapsedTime_ = (double)(CurrentTickCount_.QuadPart -
OldTickCount_.QuadPart)/TickFrquency_.QuadPart;
TotalTime_ += EllapsedTime_;
OldTickCount_ = CurrentTickCount_;
}
void CalculateEllapsedTime(const PerformanceCounter& to_intialize)
{
CurrentTickCount_ = to_intialize.CurrentTickCount();
EllapsedTime_ = (double)(CurrentTickCount_.QuadPart -
OldTickCount_.QuadPart)/TickFrquency_.QuadPart;
TotalTime_ += EllapsedTime_;
OldTickCount_ = CurrentTickCount_;
}
const LARGE_INTEGER& CurrentTickCount() const {return CurrentTickCount_;}
double EllapsedMilliseconds() const {return EllapsedTime_ * 1000;}
double EllapsedSeconds() const {return EllapsedTime_;}
double TotalTime() const {return TotalTime_;}
};
template <typename ParamT>
class EventHandlerBase1
{
public:
virtual void notify(ParamT param) = 0;
};
template <typename ListenerT,typename ParamT>
class EventHandler1 : public EventHandlerBase1<ParamT>
{
typedef void ( ListenerT::*PtrMember)(ParamT);
ListenerT* m_object;
PtrMember m_member;
public:
EventHandler1(ListenerT* object, PtrMember member)
: m_object(object), m_member(member)
{}
void notify(ParamT param)
{
(m_object->*m_member)(param);
}
};
template <typename ParamT>
class EventHandlerFunc1 : public EventHandlerBase1<ParamT>
{
typedef void ( * HandlerFunc)(ParamT);
HandlerFunc m_handler;
public:
EventHandlerFunc1(HandlerFunc f)
: m_handler(f)
{}
void notify(ParamT p)
{
m_handler(p);
}
};
template <typename ParamT>
class EventHandlerStdCallFunc1 : public EventHandlerBase1<ParamT>
{
typedef void (_stdcall * HandlerFunc)(ParamT);
HandlerFunc m_handler;
public:
EventHandlerStdCallFunc1(HandlerFunc f)
: m_handler(f)
{}
void notify(ParamT p)
{
m_handler(p);
}
};
/// <summary>
/// C++ event with one parameter
/// </summary>
template <typename ParamT>
class CppEvent1
{
typedef std::map< int, EventHandlerBase1<ParamT> *> HandlersMap;
HandlersMap m_handlers;
int m_count;
public:
CppEvent1()
: m_count(0) {}
~CppEvent1()
{
HandlersMap::iterator it = m_handlers.begin();
for(; it != m_handlers.end(); it++)
{
delete it->second;
}
}
/// <summary>
/// Attach a member function to the event
/// </summary>
template <typename ListenerT>
CppEventHandler attach(ListenerT* object,void (ListenerT::*member)(ParamT))
{
typedef void (ListenerT::*PtrMember)(ParamT);
m_handlers[m_count]=new EventHandler1<ListenerT,ParamT>(object,member);
m_count++;
return m_count-1;
}
typedef void ( * Callback)(ParamT);
/// <summary>
/// Attach a static function to the event
/// </summary>
CppEventHandler attach(Callback f)
{
m_handlers[m_count]=new EventHandlerFunc1<ParamT>(f);
m_count++;
return m_count-1;
}
typedef void (_stdcall * MngCallback)(ParamT);
/// <summary>
/// Attach a managed function to the event
/// </summary>
CppEventHandler attach(MngCallback f)
{
m_handlers[m_count]=new EventHandlerStdCallFunc1<ParamT>(f);
m_count++;
return m_count-1;
}
/// <summary>
/// Detach an event handler from the event
/// </summary>
bool detach(CppEventHandler id)
{
HandlersMap::iterator it = m_handlers.find(id);
if(it == m_handlers.end())
return false;
delete it->second;
m_handlers.erase(it);
return true;
}
/// <summary>
/// Fire the event
/// </summary>
void notify(ParamT param)
{
HandlersMap::iterator it = m_handlers.begin();
for(; it != m_handlers.end(); it++)
{
it->second->notify(param);
}
return;
}
};
CppEvent1< char > cppEvent;
class TestCppEvent
{
public:
CppEventHandler eventHandler;
int counter;
void Open()
{
eventHandler = cppEvent.attach(this,&TestCppEvent::OnEventReceived);
counter=0;
}
void Close()
{
cppEvent.detach(eventHandler);
}
void OnEventReceived(char b)
{
counter++;
}
};
class TestBind
{
public:
int counter;
TestBind():counter(0){}
void OnEventReceived(char b)
{
counter++;
}
};
class TestSignal
{
public:
int counter;
TestSignal():counter(0){}
void OnEventReceived(char b)
{
counter++;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
PerformanceCounter timer;
TestCppEvent testCppEvent;
testCppEvent.Open();
timer.Reset();
for(int i = 0; i < 10000000; i++)
{
cppEvent.notify('a');
}
timer.CalculateEllapsedTime();
testCppEvent.Close();
cout<<"Zeit[TestCppEvent]:"<<timer.EllapsedMilliseconds()<<endl;
cout<<testCppEvent.counter << endl;
TestBind testBind;
boost::function<void (char)> f =
boost::bind(&TestBind::OnEventReceived,&testBind,_1);
timer.Reset();
for(int i = 0; i < 10000000; i++)
{
f('a');
}
timer.CalculateEllapsedTime();
cout<<"Zeit[TestBind]:"<<timer.EllapsedMilliseconds()<<endl;
cout<<testBind.counter << endl;
TestSignal testSignal;
boost::signals2::signal<void (char)> sig;
sig.connect(boost::bind(&TestSignal::OnEventReceived,&testSignal,_1));;
timer.Reset();
for(int i = 0; i < 10000000; i++)
{
sig('a');
}
timer.CalculateEllapsedTime();
cout<<"Zeit[TestSignal]:"<<timer.EllapsedMilliseconds()<<endl;
cout << testSignal.counter << endl;
}
Johan Råde schrieb:
> Here is a late review.
>
>
>> * What is your evaluation of the design?
>
> Excellent. The design of Boost.signals is excellent,
> and Boost.signals2 follows the same design.
>
> Boost.signals2 should provide an optional (non-thread safe)
> compatibility mode with Boost.signals.
> This mode should not be available by default,
> but only if the user defines some macro.
> This will make porting from Boost.signals
> to Boost.signals2 much smoother.
>
> Frank also mentioned the possibility of adding
> a thread safe version of boost::trackable
> based on boost::enable_shared_from_this.
> That would be a very valuable addition to the library.
>
>
>> * What is your evaluation of the implementation?
>
> Have not looked at the implementation.
>
>
>> * What is your evaluation of the documentation?
>
> I agree with the other reviewers that it could be improved.
>
>
>> * What is your evaluation of the potential usefulness of the library?
>
> Very useful. The lack of thread safety is the main weakness of
> Boost.signals,
> and is the only point where Boost.signals is inferior to the Qt signals.
>
>
>> * Did you try to use the library? With what compiler? Did you have
>> any problems?
>
> Tried with MSVS 9.0. No problems.
>
>
>> * How much effort did you put into your evaluation? A glance? A
>> quick reading? In-depth study?
>
> Read the docs. Wrote, compiled and ran a few simple examples.
>
>
>> * Are you knowledgeable about the problem domain?
>
> Yes. I do all my work in an event-driven framework.
> The three Boost libraries I use most are smart pointers, bind and signals.
> I also have a lot of experience with the Qt signals.
>
>
>> * Do you think the library should be accepted as a Boost library?
>
> Yes. We need thread safe signals.
>
>
> Many thanks to Frank for submitting this excellent library,
> Johan Råde
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net