2012-08-10 11 views
8

Ich versuche, einen Leistungsunterschied zwischen der Verwendung von Boost.Variant und der Verwendung von virtuellen Schnittstellen zu messen. Angenommen, ich möchte verschiedene Typen von Zahlen gleichmäßig erhöhen, indem ich Boost.Variant verwende. Ich würde eine boost :: variant über int und float und einen statischen Besucher verwenden, der jeden einzelnen erhöht. Mit Klassenschnittstellen würde ich eine reine virtuelle Klassennummer und number_int- und number_float-Klassen verwenden, die daraus abgeleitet sind und eine "increment" -Methode implementieren.

Aus meinen Tests ist die Verwendung von Schnittstellen viel schneller als die Verwendung von Boost.Variant. lief ich den Code unten und erhielt diese Ergebnisse:
Virtuelle: 00: 00: 00.001028
Variant: 00: 00: 00,012081
Boost.Variant Vs Virtual Interface Leistung

Warum glauben Sie, ist dieser Unterschied? Ich dachte Boost.Variant wäre viel schneller.

** Hinweis: Normalerweise verwendet Boost.Variant Heap-Zuordnungen, um zu gewährleisten, dass die Variante immer nicht leer ist. Aber ich lese in der Boost.Variant Dokumentation, dass, wenn boost :: has_nothrow_copy true ist, es keine Heap-Zuweisungen verwendet, die Dinge wesentlich schneller machen sollten. Für int und float boost :: has_nothrow_copy ist true.

Hier ist mein Code zum Messen der beiden Ansätze gegeneinander.

#include <iostream> 

#include <boost/variant/variant.hpp> 
#include <boost/variant/static_visitor.hpp> 
#include <boost/variant/apply_visitor.hpp> 

#include <boost/date_time/posix_time/ptime.hpp> 
#include <boost/date_time/posix_time/posix_time_types.hpp> 
#include <boost/date_time/posix_time/posix_time_io.hpp> 

#include <boost/format.hpp> 

const int iterations_count = 100000; 

// a visitor that increments a variant by N 
template <int N> 
struct add : boost::static_visitor<> { 
    template <typename T>  
    void operator() (T& t) const { 
     t += N; 
    } 
}; 

// a number interface 
struct number {   
    virtual void increment() = 0; 
}; 

// number interface implementation for all types 
template <typename T> 
struct number_ : number { 
    number_(T t = 0) : t(t) {} 
    virtual void increment() { 
     t += 1; 
    } 
    T t; 
}; 

void use_virtual() { 
    number_<int> num_int; 
    number* num = &num_int; 

    for (int i = 0; i < iterations_count; i++) { 
     num->increment(); 
    } 
} 

void use_variant() { 
    typedef boost::variant<int, float, double> number; 
    number num = 0; 

    for (int i = 0; i < iterations_count; i++) { 
     boost::apply_visitor(add<1>(), num); 
    } 
} 

int main() { 
    using namespace boost::posix_time; 

    ptime start, end; 
    time_duration d1, d2; 

    // virtual 
    start = microsec_clock::universal_time(); 
    use_virtual(); 
    end = microsec_clock::universal_time(); 

    // store result 
    d1 = end - start; 

    // variant 
    start = microsec_clock::universal_time(); 
    use_variant(); 
    end = microsec_clock::universal_time(); 

    // store result 
    d2 = end - start; 

    // output 
    std::cout << 
     boost::format(
      "Virtual: %1%\n" 
      "Variant: %2%\n" 
     ) % d1 % d2; 
} 

Antwort

14

Für Interessenten, nachdem ich ein bisschen frustriert war, habe ich die Option -O2 an den Compiler und boost :: Variante war viel schneller als ein virtuelles Call.
Dank

+0

Vielen Dank für die Buchung der Follow-up, mich interessiert! –

+0

Was waren Ihre Ergebnisse und welcher Compiler? Mit Boost 1.52 und Mingw 4.7 bekomme ich eine Variante, die im Release-Modus ungefähr 8-mal langsamer ist. Seltsamerweise ist "-O2" etwas schneller als "-O3";/ – AbstractDissonance

+0

Ich benutze g ++ 4.7 und ich bin mir nicht sicher, welche Boost Version es ist, aber es ist wahrscheinlich 1.5x. Ich übergab -O2 an den Compiler und meine Ergebnisse waren: Virtual: 00: 00: 00.018806 Variante: 00: 00: 00.000001 Die meisten Male würde ich 00:00:00 auf der Variante bekommen, so dass ich iterations_count auf 10000000 setzen Ich führe diesen Test auf einer 2.8GHz Intel Core i7 CPU durch. –

4

Dies ist offensichtlich, dass -O2 reduziert die Variante Zeit, weil die ganze Schleife weg optimiert ist. Ändern Sie die Implementierung der akkumulierte Ergebnis an den Aufrufer zurück, so dass der Optimierer nicht die Schleife entfernen, und Sie werden den Unterschied erhalten:

Ausgang:
Virtuelle: 00: 00: 00,000120 = Variant 10000000
: 00: 00: 00,013483 = 10000000

#include <iostream> 

#include <boost/variant/variant.hpp> 
#include <boost/variant/static_visitor.hpp> 
#include <boost/variant/apply_visitor.hpp> 

#include <boost/date_time/posix_time/ptime.hpp> 
#include <boost/date_time/posix_time/posix_time_types.hpp> 
#include <boost/date_time/posix_time/posix_time_io.hpp> 

#include <boost/format.hpp> 

const int iterations_count = 100000000; 

// a visitor that increments a variant by N 
template <int N> 
struct add : boost::static_visitor<> { 
    template <typename T> 
    void operator() (T& t) const { 
     t += N; 
    } 
}; 

// a visitor that increments a variant by N 
template <typename T, typename V> 
T get(const V& v) { 
    struct getter : boost::static_visitor<T> { 
     T operator() (T t) const { return t; } 
    }; 
    return boost::apply_visitor(getter(), v); 
} 

// a number interface 
struct number { 
    virtual void increment() = 0; 
}; 

// number interface implementation for all types 
template <typename T> 
struct number_ : number { 
    number_(T t = 0) : t(t) {} 
    virtual void increment() { t += 1; } 
    T t; 
}; 

int use_virtual() { 
    number_<int> num_int; 
    number* num = &num_int; 

    for (int i = 0; i < iterations_count; i++) { 
     num->increment(); 
    } 

    return num_int.t; 
} 

int use_variant() { 
    typedef boost::variant<int, float, double> number; 
    number num = 0; 

    for (int i = 0; i < iterations_count; i++) { 
     boost::apply_visitor(add<1>(), num); 
    } 

    return get<int>(num); 
} 
int main() { 
    using namespace boost::posix_time; 

    ptime start, end; 
    time_duration d1, d2; 

    // virtual 
    start = microsec_clock::universal_time(); 
    int i1 = use_virtual(); 
    end = microsec_clock::universal_time(); 

    // store result 
    d1 = end - start; 

    // variant 
    start = microsec_clock::universal_time(); 
    int i2 = use_variant(); 
    end = microsec_clock::universal_time(); 

    // store result 
    d2 = end - start; 

    // output 
    std::cout << 
     boost::format(
      "Virtual: %1% = %2%\n" 
      "Variant: %3% = %4%\n" 
     ) % d1 % i1 % d2 % i2; 
} 
+3

Ist das nicht immer noch virtuell ist 2 Größenordnungen schneller? –

+0

Sind Sie sicher, dass der Compiler nicht devirtualisiert? – Brahim