Gmane
From: dan marsden <danmarsden <at> yahoo.co.uk>
Subject: Re: [fusion] question
Newsgroups: gmane.comp.lib.boost.devel
Date: 2006-05-09 21:27:38 GMT (3 years, 9 weeks, 16 hours and 59 minutes ago)
Gennadiy Rozental wrote:
>"dan marsden" <danmarsden <at> yahoo.co.uk> wrote in message
>news:20060506075932.96933.qmail <at> web25106.mail.ukl.yahoo.com...
>> Gennadiy Rozental wrote:
>>>I may not have time for review, but just one simple question: what are the
>>>advantages/disadvantages/differences in usage with
>>>stl_container<boost::variant>?
>>
>> Disclaimer: I'm not a regular user of boost::variant.
>>
>> I believe the key differences is what information is available / fixed at
>> compile time
>> rather than run time.
>
>Ok. But I would be more interrested in pratiacal approach. So my questions
>would be:
>
>1. Which tasks require fusion::vector and does not accept vector<variant>?

Tasks where compile time access to the types of the sequence elements. Code
that uses expression templates such as spirit, lambda libraries etc. would benefit
from the ability to manipulate unnamed collections of types.

Code where testing the types of the elements at runtime is inappropriate or too
expensive in terms of runtime or footprint size (see below).

>2. Which tasks require vector<variant> and does not accept fusion::vector?

If the number of elements to be inserted into the sequence cannot be decided until
runtime, then the fixed size nature of fusion sequences will not be suitable for your
programming task.

>3. Which tasks I could prefer fusion::vector for? What practical reasons?

Well anything where you require hetrogenous containers and algorithms to 
manipulate them. A few examples of where this would be useful:

*Expression template heavy libraries - such as spirit, phoenix etc.
*Libraries such as bind that may need to bundle up collections of arguments, and manipulate those
collections, this is a natural operation for a tuple lib.
*An iterator adaptor such as a zip iterator would benefit from the ability to return
tuples (fusion sequences) of references to the members of the zipped sequences.
*Anywhere you find yourself needing std::pair, but 2 elements really isn't enough,
std::pairs are really just a tuple type with 2 elements. Contrasting with using
containers of variants, you wouldn't expect to use a 2 element container of variants to
insert into a std::map.

>And if this is performance what is a performance advantage 1%, 10%, 50%?

I've attached a simple performance test to compare using fusion versus a container of
variants, to contrast the 2 approaches. Output below:

VC8.0

Short test ... 
80
80
Fusion vector size                                : 12
Variant vector size (includes vector size + data) : 48
Fusion vector time         : 5.11855e-009
Variant time               : 1.11818e-007
Long test ... 
480
480
Fusion vector time         : 1.76728e-008
Variant time               : 5.51224e-007

//////////////////////////////////
VC7.1

Short test ... 
80
80
Fusion vector size                                : 12
Variant vector size (includes vector size + data) : 48
Fusion vector time         : 1.86265e-008
Variant time               : 1.33991e-007
Long test ... 
480
480
Fusion vector time         : 9.67979e-008
Variant time               : 6.55174e-007

//////////////////////////////////
g++3.4

Short test ... 
80
80
Fusion vector size                                : 16
Variant vector size (includes vector size + data) : 44
Fusion vector time         : 5.49555e-08
Variant time               : 4.74453e-08
Long test ... 
480
480
Fusion vector time         : 2.90394e-07
Variant time               : 2.08855e-07

///////////////////////////////////

Icl 9.0:----------------------------------------------
icl -Ox -Qipo -EHsc -IC:\CVS\fusion-2\spirit -IC:\CVS\Boost
fusion_vs_variant.cpp

Short test ...
80
80
Fusion vector size                                : 12
Variant vector size (includes vector size + data) : 48
Fusion vector time         : 1.16378e-008
Variant time               : 4.09484e-008
Long test ...
480
480
Fusion vector time         : 6.52075e-008
Variant time               : 1.52826e-007

As you can see, footprint size is consistently about 4x smaller for Fusion versus
the variant approach. Performance is signicantly faster in all cases, except the gcc
test, where variant actually outperforms. We've not traced why gcc is failing to
optimize the test case as well as the other compilers.

Of course a benchmark and may not reflect the usage patterns in a specific
application...

>4. Could you compare from usdabiltiy standpoing all the operation shared by
>this two approaches?

That is an extremely broad question, did you have any specific concern in mind?

Hope that was helpful
Cheers
Dan

/*=============================================================================
    Copyright (c) 2006 Eric Niebler

    Use, modification and distribution is subject to the Boost Software
    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#define FUSION_MAX_VECTOR_SIZE 25

#ifdef _MSC_VER
// inline aggressively
# pragma inline_recursion(on) // turn on inline recursion
# pragma inline_depth(255)    // max inline depth
#endif

#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/timer.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/variant.hpp>

namespace test
{
    struct accumulator : boost::static_visitor<>
    {
        accumulator(int &i_)
        : i(i_)
        {}

        template<typename T>
        void operator()(T const &t) const
        {
            this->i += t;
        }

        int &i;
    };

    struct variant_accumulator
    {    
        variant_accumulator(int &i_)
        : i(i_)
        {}

        template <typename Variant>
        void operator()(Variant const& v) const
        {
            boost::apply_visitor( accumulator(i), v );
        }

        int &i;
    };

    int const REPEAT_COUNT = 10;

    template<typename T>
    double time_for_each_variant(T const &t, int &j)
    {
        boost::timer tim;
        int i = 0;
        long long iter = 65536;
        long long counter, repeats;
        double result = 0;
        double run;
        do
        {
            tim.restart();
            for(counter = 0; counter < iter; ++counter)
            {
                i = 0;
                std::for_each(t.begin(), t.end(), variant_accumulator(i));
                static_cast<void>(i);
            }
            result = tim.elapsed();
            iter *= 2;
        } while(result < 0.5);
        iter /= 2;

        // repeat test and report least value for consistency:
        for(repeats = 0; repeats < REPEAT_COUNT; ++repeats)
        {
            tim.restart();
            for(counter = 0; counter < iter; ++counter)
            {
                i = 0;
                std::for_each(t.begin(), t.end(), variant_accumulator(i));
                j += i;
            }
            run = tim.elapsed();
            result = (std::min)(run, result);
        }
        std::cout << i << std::endl;
        return result / iter;
    }

    template<typename T>
    double time_for_each_fusion(T const &t, int &j)
    {
        boost::timer tim;
        int i = 0;
        long long iter = 65536;
        long long counter, repeats;
        double result = 0;
        double run;
        do
        {
            tim.restart();
            for(counter = 0; counter < iter; ++counter)
            {
                i = 0;
                boost::fusion::for_each(t, accumulator(i));
                static_cast<void>(i);
            }
            result = tim.elapsed();
            iter *= 2;
        } while(result < 0.5);
        iter /= 2;

        // repeat test and report least value for consistency:
        for(repeats = 0; repeats < REPEAT_COUNT; ++repeats)
        {
            tim.restart();
            for(counter = 0; counter < iter; ++counter)
            {
                i = 0;
                boost::fusion::for_each(t, accumulator(i));
                j += i;
            }
            run = tim.elapsed();
            result = (std::min)(run, result);
        }
        std::cout << i << std::endl;
        return result / iter;
    }

    int test_short()
    {
        int j = 0;
        typedef int T0;
        typedef char T1;
        typedef short T2;
        typedef long T3;
        typedef boost::fusion::vector<T0,T1,T2,T3> fusion_vector_type;
        fusion_vector_type const l(3,42,6,29);

        double fusion_time =
            time_for_each_fusion( l, j );

        typedef boost::variant<T0,T1,T2,T3> variant_type;
        std::vector<variant_type> v;
        v.push_back(3);
        v.push_back(42);
        v.push_back(6);
        v.push_back(29);

        double variant_time =
            time_for_each_variant( v, j );

        std::cout << "Fusion vector size                                : " << sizeof(fusion_vector_type) << std::endl;
        std::cout << "Variant vector size (includes vector size + data) : " <<
(sizeof(variant_type)*4)+sizeof(v) << std::endl;

        std::cout << "Fusion vector time         : " << fusion_time << std::endl;
        std::cout << "Variant time               : " << variant_time << std::endl;

        return j;
    }

    int test_long()
    {
        int j = 0;
        typedef int T0;
        typedef char T1;
        typedef short T2;
        typedef long T3;

boost::fusion::vector<T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3> 
        const l(
            3,42,6,29
           ,3,42,6,29
           ,3,42,6,29
           ,3,42,6,29
           ,3,42,6,29
           ,3,42,6,29);

        double fusion_time =
            time_for_each_fusion( l, j );

        typedef boost::variant<T0,T1,T2,T3> variant_type;
        std::vector<variant_type> v;
        for (int i = 0; i < 6; ++i)
        {
            v.push_back(3);
            v.push_back(42);
            v.push_back(6);
            v.push_back(29);
        }

        double variant_time =
            time_for_each_variant( v, j );

        std::cout << "Fusion vector time         : " << fusion_time << std::endl;
        std::cout << "Variant time               : " << variant_time << std::endl;

        return j;
    }
}

int main()
{
    std::cout << "Short test ... \n";
    int i = test::test_short();

    std::cout << "Long test ... \n";
    int j = test::test_long();

    return i + j;
}
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost