【问题标题】:Boost.Variant Vs Virtual Interface PerformanceBoost.Variant 与虚拟接口性能
【发布时间】:2012-08-08 01:19:47
【问题描述】:

我正在尝试衡量使用 Boost.Variant 和使用虚拟接口之间的性能差异。例如,假设我想统一增加不同类型的数字,使用 Boost.Variant 我将使用 boost::variant over int 和 float 以及一个增加每个数字的静态访问者。使用类接口,我将使用纯虚拟类 number 和 number_int 和 number_float 类,它们派生自它并实现“增量”方法。

根据我的测试,使用接口比使用 Boost.Variant 快得多。 我运行了底部的代码并收到了这些结果:
虚拟:00:00:00.001028
变体:00:00:00.012081

你认为为什么会有这种差异?我认为 Boost.Variant 会快很多。

** 注意:通常 Boost.Variant 使用堆分配来保证变体总是非空的。但是我在 Boost.Variant 文档中读到,如果 boost::has_nothrow_copy 为真,那么它不会使用堆分配,这应该会使事情变得更快。对于 int 和 float boost::has_nothrow_copy 为 true。

这是我用于衡量这两种方法的代码。

#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;
}

【问题讨论】:

    标签: performance boost interface virtual variant


    【解决方案1】:

    很明显,-O2 减少了变体时间,因为整个循环都被优化掉了。更改实现以将累积的结果返回给调用者,这样优化器就不会删除循环,您会得到真正的区别:

    输出:
    虚拟:00:00:00.000120 = 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;
    }
    

    【讨论】:

    • 那不是仍然显示虚拟速度快 2 个数量级吗?
    • 你确定编译器没有去虚拟化吗?
    【解决方案2】:

    对于那些感兴趣的人,在我有点沮丧之后,我将选项 -O2 传递给了编译器,并且 boost::variant 比虚拟调用要快得多。
    谢谢

    【讨论】:

    • 感谢发布后续,我很感兴趣!
    • 你的结果是什么,什么编译器?使用 Boost 1.52 和 Mingw 4.7 我得到变体在发布模式下慢了大约 8 倍。奇怪的是-O2-O3 稍快;/
    • 我正在使用 g++ 4.7,我不确定 Boost 版本,但它可能是 1.5 倍。我将 -O2 传递给编译器,我的结果是: Virtual: 00:00:00.018806 Variant: 00:00:00.000001 大多数时候我会在变体上得到 00:00:00,所以我将 iterations_count 设置为 10000000。我我在 2.8Ghz Intel Core i7 CPU 上运行此测试。
    • 另外我刚刚注意到,如果我在 use_virtual 测试之前进行 use_variant 测试,那么变体会减慢到大约 00:00:00.000426,这很奇怪..
    猜你喜欢
    • 2012-05-09
    • 1970-01-01
    • 2017-07-17
    • 2016-06-18
    • 2010-11-24
    • 1970-01-01
    • 2013-11-21
    • 2015-07-05
    相关资源
    最近更新 更多