Die Boost C++ Bibliotheken

Kapitel 38. Boost.Timer

Boost.Timer stellt Uhren zur Verfügung, um die Ausführungsgeschwindigkeit von Code zu messen. Auf den ersten Blick konkurriert die Bibliothek mit Boost.Chrono. Während Boost.Chrono jedoch Uhren zur beliebigen Zeitmessung zur Verfügung stellt, geht es bei Boost.Timer explizit um die Ausführungsgeschwindigkeit von Code. Dazu verwendet Boost.Timer unter der Haube Uhren von Boost.Chrono. Wann immer Sie die Performance Ihres Codes messen möchten, sollten Sie sich Boost.Timer zuwenden und nicht Boost.Chrono.

Boost.Timer liegt seit den Boost-Bibliotheken 1.48.0 in einer neuen zweiten Version vor. In Boost.Timer 2 gibt es lediglich eine Headerdatei: Sie binden immer boost/timer/timer.hpp ein. Es existiert auch eine Headerdatei boost/timer.hpp, die Sie jedoch nicht einbinden dürfen. Diese Headerdatei gehört zur ersten Version von Boost.Timer, die nicht mehr verwendet werden sollte.

Die von Boost.Timer angebotenen Uhren sind in den Klassen boost::timer::cpu_timer und boost::timer::auto_cpu_timer implementiert. boost::timer::auto_cpu_timer ist von boost::timer::cpu_timer abgeleitet und stoppt im Destruktor automatisch die Zeit, um anschließend eine Meldung auszugeben.

Im Beispiel 38.1 wird Ihnen zuerst die Klasse boost::timer::cpu_timer vorgestellt. Dieses wie auch die folgenden Beispielprogramme führen mathematische Berechnungen aus, damit garantiert Zeit vergeht, bevor die Beispielprogramme enden. Andernfalls würde jeweils 0 gemessen und die Uhren von Boost.Timer nur schlecht vorgestellt werden können.

Beispiel 38.1. Zeit messen mit boost::timer::cpu_timer
#include <boost/timer/timer.hpp>
#include <iostream>
#include <cmath>

using namespace boost::timer;

int main()
{
  cpu_timer timer;

  for (int i = 0; i < 1000000; ++i)
    std::pow(1.234, i);
  std::cout << timer.format() << '\n';
}

Wenn Sie die Klasse boost::timer::cpu_timer instanziieren, wird automatisch die Zeitmessung gestartet. Sie können jederzeit die Methode format() aufrufen, die die bis zu diesem Moment verstrichene Zeit in einem String zurückgibt. Wenn Sie Beispiel 38.1 ausführen, wird beispielsweise 0.099170s wall, 0.093601s user + 0.000000s system = 0.093601s CPU (94.4%) auf die Standardausgabe ausgegeben.

Boost.Timer misst sowohl die tatsächliche Zeit als auch die CPU-Zeit. Die tatsächliche Zeit gibt an, wie viel Zeit in Wirklichkeit verstrichen ist. Sie könnten diese Zeit mit einer Stoppuhr messen. Die CPU-Zeit gibt den Anteil der tatsächlichen Zeit an, in der Code des Programms ausgeführt wurde. So steht zum einen einem Programm auf heutigen Multitasking-Systemen nicht ununterbrochen ein Prozessor zur Verfügung. Zum anderen kann ein Programm auch absichtlich anhalten, weil zum Beispiel auf eine Benutzereingabe gewartet wird. In diesen Fällen läuft die tatsächliche Zeit weiter, nicht aber die CPU-Zeit.

Boost.Timer unterscheidet bei der CPU-Zeit außerdem zwischen der Zeit, in der Code im User Space und im Kernel Space ausgeführt wird. Zum Kernel Space gehört Code, der Teil des Betriebssystems ist. Der User Space ist entsprechend Code, der nicht zum Betriebssystem gehört. Dazu zählt der von Ihnen geschriebene Programmcode als auch Code in Bibliotheken Dritter. So befinden sich zum Beispiel die Boost-Bibliotheken ebenfalls im User Space. Je mehr Funktionen des Betriebssystems Sie aufrufen und je mehr Zeit diese Funktionen benötigen, umso größer wird der Anteil der Zeit im Kernel Space an der CPU-Zeit.

Beispiel 38.2. Zeitmessung unterbrechen und fortsetzen
#include <boost/timer/timer.hpp>
#include <iostream>
#include <cmath>

using namespace boost::timer;

int main()
{
  cpu_timer timer;

  for (int i = 0; i < 1000000; ++i)
    std::pow(1.234, i);
  std::cout << timer.format() << '\n';

  timer.stop();

  for (int i = 0; i < 1000000; ++i)
    std::pow(1.234, i);
  std::cout << timer.format() << '\n';

  timer.resume();

  for (int i = 0; i < 1000000; ++i)
    std::pow(1.234, i);
  std::cout << timer.format() << '\n';
}

boost::timer::cpu_timer bietet mit stop() und resume() zwei Methoden an, um die Zeitmessung zu unterbrechen und fortzusetzen. Das führt im Beispiel 38.2 dazu, dass die Zeit, die die zweite for-Schleife zur Ausführung benötigt, nicht gemessen wird. Das ist gleichbedeutend mit einer Stoppuhr, die angehalten wird und nach einiger Zeit weiterlaufen darf. So wird beim zweiten Aufruf von format() im Beispiel 38.2 eine tatsächliche Zeit und eine CPU-Zeit ausgegeben, als würde die zweite for-Schleife im Programm nicht existieren.

boost::timer::cpu_timer bietet auch eine Methode start(). Wenn Sie anstelle von resume() start() aufrufen, beginnt die Zeitmessung wieder bei null. Der Konstruktor von boost::timer::cpu_timer ruft start() auf, weswegen die Zeitmessung sofort beginnt, wenn Sie boost::timer::cpu_timer instanziieren.

Beispiel 38.3. Tatsächliche Zeit und CPU-Zeit als Tuple erhalten
#include <boost/timer/timer.hpp>
#include <iostream>
#include <cmath>

using namespace boost::timer;

int main()
{
  cpu_timer timer;

  for (int i = 0; i < 1000000; ++i)
    std::pow(1.234, i);

  cpu_times times = timer.elapsed();
  std::cout << times.wall << '\n';
  std::cout << times.user << '\n';
  std::cout << times.system << '\n';
}

Während format() die gemessene tatsächliche Zeit und die CPU-Zeit als String zurückgibt, können Sie die Zeiten auch in einem Tuple erhalten. boost::timer::cpu_timer bietet dazu die Methode elapsed() an. elapsed() gibt ein Tuple vom Typ boost::timer::times zurück. Wie im Beispiel 38.3 zu sehen, besitzt dieses Tuple drei Eigenschaften wall, user und system. Die Eigenschaften geben die tatsächliche Zeit und die CPU-Zeit in Nanosekunden an. Es handelt sich um Zahlen vom Typ boost::int_least64_t.

boost::timer::times bietet auch eine Methode clear() an, um die Eigenschaften wall, user und system auf 0 zu setzen.

Beispiel 38.4. Automatisch messen mit boost::timer::auto_cpu_timer
#include <boost/timer/timer.hpp>
#include <cmath>

using namespace boost::timer;

int main()
{
  auto_cpu_timer timer;

  for (int i = 0; i < 1000000; ++i)
    std::pow(1.234, i);
}

Sie können die tatsächliche Zeit und die CPU-Zeit eines Code-Blocks mit boost::timer::auto_cpu_timer messen. Weil diese Klasse im Destruktor automatisch die Zeit stoppt und eine Meldung auf die Standardausgabe ausgibt, macht Beispiel 38.4 das Gleiche wie Beispiel 38.1.

Die Klasse boost::timer::auto_cpu_timer bietet mehrere Konstruktoren an, um zum Beispiel angeben zu können, auf welchen Stream die Datenausgabe erfolgen soll. Standardmäßig ist dies std::cout.

Sie können sowohl für die Klasse boost::timer::cpu_timer als auch für boost::timer::auto_cpu_timer angeben, wie Meldungen formatiert werden sollen. Boost.Timer bietet einige wenige Sonderzeichen zur Formatierung an, die ähnlich wie Sonderzeichen von Boost.Format oder std::printf() funktionieren. Die Dokumentation von Boost.Timer enthält eine entsprechende Übersicht.