Boost logo

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