Wenn Boost.Signals zu langsam ist...

Inhaltsverzeichnis[Verbergen]

...dann sollte man sich nach Alternativen umsehen. Gleich vorweg, das soll jetzt kein Aufruf sein Boost.Signals zu vermeiden sondern nur einen Anstoß zur gezielten Verwendung geben. Ich bin schließlich selbst begeisterter Boost Nutzer. Wer Boost oder Boost.Signals nicht kennt sollte sich einmal auf der Boost-Homepage bzw. im speziellen dem Teil zu Boost.Signals. Kurz gesagt es ist eine sehr umfangreiche "Signals and Slots" Bibliothek für C++. Vor kurzem bin ich dann einmal auf eine Seite gekommen auf der gestanden ist das Boost eigentlich sehr langsam ist. Anfangs war ich misstrauisch da Boost Bibliotheken im allgemeinen doch recht optimiert sind. Also hab ich kurzerhand einfach einmal ein einfaches Testprogramm erstellt und ein paar Benchmarks durchgeführt.

1. Der Test

Für den Test habe ich neben Boost.Signals noch libsigc++ verwendet, da es scheinbar einen nicht so schlechten Ruf hat. Zusätzlich habe ich noch mit Boost.Function in Kombination mit std::list eine einfache Implementation selbst geschrieben. Dann habe ich für jeden Typ ein bestimmte Anzahl von Slots registriert und dann eine bestimmte Anzahl oft aufgerufen. Dabei habe ich dann einfach die dafür benötigte Zeit gemessen und bin auf die folgenden Werte bekommen (Die Werte in der Tabelle von Oben nach Unten sind in der Grafik von Links nach Rechts angeordnet):

SlotsCallsBoostSigc++FunctionList
110000000.169s0.0702s0.00669s
101000000.0428s0.0104s0.0059s
100100000.0324s0.00483s0.00596s
100010000.0337s0.00847s0.00783s
100001000.0485s0.0179s0.00872s
100000100.111s0.0725s0.0611s
100000010.112s0.0723s0.0614s
Signals and Slots Benchmarks Signals and Slots Benchmarks mit meinem Core2Quad Q6600

Wie wir erkennen können braucht Boost immer um einiges länger und das merkt man besonders stark wenn viele Slots mit wenigen Aufrufen verwendet werden. Daraus können wir schließen das Boost.Signals einigen Aufwand treibt wenn man ein Signal aufruft. libsigc++ ist anscheinend performanter aufgebaut und braucht maximal zwei Drittel der Zeit von Boost - meist sogar viel weniger. Am Besten schneidet jedoch die Version mit std::list und Boost.Function ab. Den größten Vorsprung hat sie bei wenigen Slots mit vielen Aufrufen. Was in diesen Test jedoch nicht eingeflossen ist ist die umfangreiche Funktionalität vor allem von Boost.Signals. Doch für diese Features zahlt man mit längeren Ausführungszeiten. Wenn wir diese ganzen Features also nicht brauchen dann können wir auch eine viele einfachere und schnellere Implementation verwenden, zumindest wenn es sich um zeitkritische Anwendungen handelt.

2. Eine einfache und schnelle Alternative

Für viele Anwendungen reicht es aus wenn wir einfach nur eine unbestimmte Anzahl von Slots bei einem Signal registrieren und diese dann alle auf einmal aufrufen können. Für diesen Zweck habe ich einen sehr einfachen aber effektiven Weg gewählt. Zum halten der einzelnen Funktionen verwenden wir Boost.Function. Damit können wir einfach definieren welche Signatur die Slots haben sollen. Bis jetzt können wir aber nur einen Slot halten was oft nicht ausreicht, weshalb wir einfache mehrere Boost.Function Objekte in einen Container packen. Gut geeignet ist dafür zum Beispiel die std::list aus der C++ Standard Bibliothek. Im folgenden Codeausschnitt tun wir eigentlich einmal nichts anderes als natürlich zuerst die benötigten Bibliotheken einzubinden und anschließend einen neue Klasse für unser Signal definieren. Zusätzlich verwenden wir noch ein Makro um uns das Tippen einer Schleife für das Aufrufen der einzelnen Slots zu ersparen.

#include <list>
#include <boost/function.hpp>

namespace tp_fast_signal
{
  template <class Signature>
    struct signal: public std::list<boost::function<Signature> >{};
}

// Abkürzung für die Schleife zum Aufrufen
#define CALL_SIGNALS( SIG, REF, ARGS )\
  for( tp_fast_signal::signal<SIG>::iterator it = REF.begin();\
       it != REF.end(); ++it )\
    (*it)ARGS;

Eigentlich war das schon alles was wir brauchen um unsere sehr einfache Signal und Slot Implementation zu verwenden. Die Syntax ist zwar nicht ganz gleich wie bei Boost.Signals aber das sollten wir für die Zeitersparnis verkraften können. Jetzt also ein einfaches Verwendungsbeispiel:

#include <iostream>
// ... Code von vorher

// Testfunktion zum Registrieren als Slots
void test( int& i )
{
  i += 1;
}

int main()
{
  tp_fast_signal::signal<void(int&)> test_signal; // Signal erstellen

  // 2 Slots registrieren:
  test_signal.push_back(&test);
  test_signal.push_back(&test);

  int test_int = 0;

  // Alle Slots aufrufen
  CALL_SIGNALS( void(int&), test_signal, (test_int) );

  // Und noch testweise den aktuellen Wert von test_int ausgeben
  std::cout << "test_int=" << test_int << std::endl;
  
  return 0;
}

Wie wir erkennen ist es nicht schwer diesen Code zu verwenden und man erhält dadurch einen ordentlichen Geschwindigkeitsvorteil. Aufpassen muss man nur bei CALL_SIGNALS dass man auch immer die Klammern um die Argumente setzt, auch wenn kein oder ein Argument verwendet werden, da diese Implementation sonst nicht funktioniert. Man kann natürlich noch einiges verbessern und zum Beispiel eine 'connect'-Methode implementieren und eventuell auch ein 'disconnect' aber so weit möchte ich hier nicht gehen.

3. Zum Schluss...

... möchte ich noch einmal erwähnen das ich die Boost Bibliotheken und Boost.Signal sehr gerne verwende und auch euch empfehle sie zu verwenden, da man mit ihnen sehr effektiv programmieren kann. Wenn wir jedoch die vielen Features von Boost.Signal nicht benötigen und sehr zeitkritische Anwendungen schreiben die viele Signale und Slots verwenden dann kann es einen durchaus einen beachtlichen Geschwindigkeitsvorteil geben wenn wir die Signale mit einer Alternative wie der gerade besprochenen realisieren.

4. Downloads

Im Anschluss gibt es noch den Code für den Test und die Signal Implementation zum Downloaden. Wenn jemand Verbesserungs- oder Codevorschläge hat bitte über das Kontaktformular oder per Email an mich schicken. Weiters würden mich noch Testergebnisse mit anderen Compilern/Plattformen/Computern interessieren.

icon Fast Signal und Signal Benchmark (2.94 kB 2009-05-11 00:23:32)

Aktualisiert ( Samstag, 06. Februar 2010 um 16:54 Uhr )