Boost logo

Boost Users :

Subject: Re: [Boost-users] [serialization] Runtime overhead of serialization archives
From: Georg Gast (georg_at_[hidden])
Date: 2016-09-25 04:38:20


Am 25.09.2016 um 07:05 schrieb Ernest Zaslavsky:
> I guess you are still comparing release to debug version. I've ran your code and this is what I've got
>
> Win7 x64, VS2015 Update3, Release, x64
>
> Run on (8 X 3392 MHz CPU s)
> 09/25/16 07:36:53
> Benchmark Time CPU Iterations
> ------------------------------------------------------------
> to_wire_xml/8 39564 ns 39980 ns 17949 195.409kB/s
> to_wire_xml/64 98915 ns 98035 ns 7479 637.527kB/s
> to_wire_xml/512 583376 ns 583961 ns 1122 856.222kB/s
> to_wire_xml/4k 4494721 ns 4428415 ns 155 903.258kB/s
> to_wire_xml/32k 35621888 ns 35100225 ns 20 911.675kB/s
> to_wire_xml/256k 285296564 ns 280801800 ns 2 911.675kB/s
> to_wire_xml/2M 2294702350 ns 2293214700 ns 1 893.069kB/s
> to_wire_xml/4M 4596100456 ns 4586429400 ns 1 893.069kB/s
> from_wire_xml/8 44701 ns 44361 ns 15473 176.11kB/s
> from_wire_xml/64 97779 ns 98035 ns 7479 637.527kB/s
> from_wire_xml/512 523956 ns 530403 ns 1000 942.679kB/s
> from_wire_xml/4k 3919513 ns 3877482 ns 173 1031.6kB/s
> from_wire_xml/32k 30990532 ns 31200200 ns 22 1025.63kB/s
> from_wire_xml/256k 248254367 ns 249601600 ns 3 1025.63kB/s
> from_wire_xml/2M 1990579271 ns 1981212700 ns 1 1033.71kB/s
> from_wire_xml/8M 7927240207 ns 7924850800 ns 1 1033.71kB/s
> to_wire_text/8 13381 ns 13142 ns 49857 594.483kB/s
> to_wire_text/64 31969 ns 31985 ns 22436 1.90827MB/s
> to_wire_text/512 180335 ns 179751 ns 4079 2.71643MB/s
> to_wire_text/4k 1363654 ns 1375560 ns 499 2.83975MB/s
> to_wire_text/32k 10990438 ns 10968820 ns 64 2.84898MB/s
> to_wire_text/256k 86883137 ns 88400567 ns 9 2.82804MB/s
> to_wire_text/2M 696132001 ns 686404400 ns 1 2.91373MB/s
> to_wire_text/4M 1398212634 ns 1388408900 ns 1 2.881MB/s
> from_wire_text/8 11158 ns 11195 ns 64102 697.873kB/s
> from_wire_text/64 25274 ns 25588 ns 28045 2.38534MB/s
> from_wire_text/512 138245 ns 137666 ns 4986 3.54685MB/s
> from_wire_text/4k 1047166 ns 1046497 ns 641 3.73269MB/s
> from_wire_text/32k 8304279 ns 8320053 ns 90 3.75599MB/s
> from_wire_text/256k 66510527 ns 66654973 ns 11 3.75066MB/s
> from_wire_text/2M 533393808 ns 530403400 ns 1 3.77071MB/s
> from_wire_text/4M 1055956857 ns 1060806800 ns 1 3.77071MB/s
> to_wire_binary/8 5444 ns 5460 ns 100000 1.39732MB/s
> to_wire_binary/64 5411 ns 5424 ns 112179 11.2538MB/s
> to_wire_binary/512 5523 ns 5563 ns 112179 87.7797MB/s
> to_wire_binary/4k 5966 ns 5980 ns 112179 653.244MB/s
> to_wire_binary/32k 28940 ns 29412 ns 24929 1062.5MB/s
> to_wire_binary/256k 251626 ns 250358 ns 2804 998.569MB/s
> to_wire_binary/2M 2548630 ns 2540925 ns 264 787.115MB/s
> to_wire_binary/4M 6361041 ns 6407184 ns 112 624.299MB/s
> from_wire_binary/8 5363 ns 5284 ns 112179 1.44375MB/s
> from_wire_binary/64 5371 ns 5460 ns 100000 11.1785MB/s
> from_wire_binary/512 5386 ns 5460 ns 100000 89.4282MB/s
> from_wire_binary/4k 5483 ns 5424 ns 112179 720.244MB/s
> from_wire_binary/32k 7685 ns 7649 ns 89743 3.98998GB/s
> from_wire_binary/256k 25332 ns 25588 ns 28045 9.54136GB/s
> from_wire_binary/2M 620654 ns 625672 ns 1122 3.12164GB/s
> from_wire_binary/4M 1306333 ns 1306960 ns 561 2.98881GB/s
>
Dear Earnest,
Thanks for running my code :)

have you run my code from the post at 23.9.16 19:41 (make_array)?

In your result the XML settles at about 2.9 MB/sec (from wire) and the
text archive at about 3.8 MB/sec (from wire).

My guess from this values is, that you testes the version with
make_array in the serialization functions. I realized this DEBUG thing
too and i fixed it in the meantime. On my desktop workstation

The make_binary_object improved the performance a lot.

Here is the current version of the source from my homepage

   Georg

<code>
// STL Archive + Stuff
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/unique_ptr.hpp>

// include headers that implement a archives in xml/text/binary format
#include <boost/archive/archive_exception.hpp>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

// IO stream for the to/from wire functions
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>

#include <memory>
#include <cstdint>
#include <vector>

#include <benchmark/benchmark.h>

// the step interval for the benchmarks
static const int range_mult = 4;
static const int range_max_step = 20;

// the test structure
struct ev_test
{
        ev_test(size_t s = 0)
        {
                m_data.resize(s);
                for (auto &c : m_data)
                        c = 1;
        }

        std::vector<uint8_t> m_data;
};

//-----------------------------------------------------------------------------
// Type carrier and its support
//----------------------------------------------------------------------------
namespace net
{
        using packet = std::vector<char>; // a packet on the wire

        class carrier_visitor_base;

        class carrier_base // the base in the queue
        {
        public:
                using ptr = std::unique_ptr<carrier_base>;

                virtual ~carrier_base() {}

                virtual void accept(carrier_visitor_base *p_visitor) = 0;
        };

        template <typename T>
        class carrier;

        class carrier_visitor_base
        {
        public:
                virtual ~carrier_visitor_base() {}

                virtual void handle(carrier<ev_test> *p_evt) = 0;
                virtual void handle(carrier<char> *p_evt) = 0;
                virtual void handle(carrier<int> *p_evt) = 0;
        };

        template <typename T>
        class carrier : public carrier_base // the specific carrier
        {
        public:
                explicit carrier() : m_data() {}

                explicit carrier(const T &data) : m_data(data) {}
                virtual void accept(carrier_visitor_base *p_visitor) override
                {
                        p_visitor->handle(this);
                }

                T &data() { return m_data; }
        private:
                T m_data;
        };
} // ns net

//----------------------------------------------------------------------------
// external serialization function
//----------------------------------------------------------------------------
BOOST_SERIALIZATION_SPLIT_FREE(ev_test)
namespace boost
{
        namespace serialization
        {
                // serialization function for carrier_base
                template <class Archive>
                void serialize(Archive &ar, net::carrier_base &t, const unsigned int
                        version)
                {
                }

                // serialization function for net::carrier<T>
                template <class Archive, typename T>
                void serialize(Archive &ar, net::carrier<T> &t, const unsigned int
version)
                {
                        ar &boost::serialization::make_nvp(
                                "carrier_base",
                                boost::serialization::base_object<net::carrier_base>(t));

                        auto &data = t.data();
                        ar &BOOST_SERIALIZATION_NVP(data);
                }

                // serialization function for ev_test
                template <class Archive>
                inline void save(Archive &ar, const ev_test &t, const unsigned int
version)
                {
                        size_t size = t.m_data.size();
                        ar &BOOST_SERIALIZATION_NVP(size);
                        ar &boost::serialization::make_nvp(
                                "m_data",
                        
boost::serialization::make_binary_object(const_cast<uint8_t*>(t.m_data.data()),
t.m_data.size()));
                }
                template <class Archive>
                inline void load(Archive &ar, ev_test &t, const unsigned int version)
                {
                        size_t size = 0;
                        ar &BOOST_SERIALIZATION_NVP(size);
                        t.m_data.resize(size);

                        ar &boost::serialization::make_nvp(
                                "m_data",
                                boost::serialization::make_binary_object(t.m_data.data(),
t.m_data.size()));
                }
        }
}

// we must export all carrier
BOOST_SERIALIZATION_SHARED_PTR(net::carrier<ev_test>)
BOOST_CLASS_EXPORT(net::carrier<ev_test>)

//----------------------------------------------------------------------------
// the traits for the boost serialization tests
//----------------------------------------------------------------------------
struct boost_xml_trait
{
        static const char *name() { return "boost_xml_test: ev_test: "; }

        typedef boost::archive::xml_oarchive oarchive;
        typedef boost::archive::xml_iarchive iarchive;
};

struct boost_text_trait
{
        static const char *name() { return "boost_text_test: ev_test: "; }

        typedef boost::archive::text_oarchive oarchive;
        typedef boost::archive::text_iarchive iarchive;
};

struct boost_binary_trait
{
        static const char *name() { return "boost_binary_test: ev_test: "; }

        typedef boost::archive::binary_oarchive oarchive;
        typedef boost::archive::binary_iarchive iarchive;
};

template <typename archive_trait>
struct boost_test
{
        static const char *name() { return archive_trait::name(); }
        static size_t msg_size() { return 600; }

        // throws boost::archive::archive_exception
        template <typename T>
        static net::packet to_wire(const T &data)
        {
                using namespace boost::iostreams;

                using T1 = typename std::remove_cv<T>::type;
                using BT = typename std::remove_reference<T1>::type;
                net::carrier_base::ptr p_carrier =
                        std::make_unique<net::carrier<BT>>(data);

                net::packet p;
                p.reserve(msg_size());
                {
                        back_insert_device<net::packet> sink(p);
                        stream<back_insert_device<net::packet>> os{ sink };

                        typename archive_trait::oarchive oa(os);
                        oa << BOOST_SERIALIZATION_NVP(p_carrier);
                }
                return p;
        }

        // throws boost::archive::archive_exception
        static net::carrier_base::ptr from_wire(const net::packet &data)
        {
                using namespace boost::iostreams;
                array_source source{ data.data(), data.size() };
                stream<array_source> is{ source };

                net::carrier_base::ptr p_carrier;
                typename archive_trait::iarchive ia(is); // this takes the most time
                ia >> BOOST_SERIALIZATION_NVP(p_carrier);

                return p_carrier;
        }
};

//----------------------------------------------------------------------------
// XML
//----------------------------------------------------------------------------
static void to_wire_xml(benchmark::State &state)
{
        std::locale::global(std::locale("C"));
        ev_test data(state.range_x());

        while (state.KeepRunning())
        {
                boost_test<boost_xml_trait>::to_wire(data);
        }

        state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
                state.range_x());
}
BENCHMARK(to_wire_xml)->Range(8, range_mult << range_max_step);

static void from_wire_xml(benchmark::State &state)
{
        std::locale::global(std::locale("C"));
        auto buffer =
                boost_test<boost_xml_trait>::to_wire(ev_test(state.range_x()));

        while (state.KeepRunning())
        {
                boost_test<boost_xml_trait>::from_wire(buffer);
        }

        state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
                state.range_x());
}
BENCHMARK(from_wire_xml)->Range(8, range_mult << range_max_step);

//----------------------------------------------------------------------------
// Text
//----------------------------------------------------------------------------
static void to_wire_text(benchmark::State &state)
{
        std::locale::global(std::locale("C"));
        ev_test data(state.range_x());

        while (state.KeepRunning())
        {
                boost_test<boost_text_trait>::to_wire(data);
        }

        state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
                state.range_x());
}
BENCHMARK(to_wire_text)->Range(8, range_mult << range_max_step);

static void from_wire_text(benchmark::State &state)
{
        std::locale::global(std::locale("C"));
        auto buffer =
                boost_test<boost_text_trait>::to_wire(ev_test(state.range_x()));

        while (state.KeepRunning())
        {
                boost_test<boost_text_trait>::from_wire(buffer);
        }

        state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
                state.range_x());
}
BENCHMARK(from_wire_text)->Range(8, range_mult << range_max_step);

//----------------------------------------------------------------------------
// Binary
//----------------------------------------------------------------------------
static void to_wire_binary(benchmark::State &state)
{
        std::locale::global(std::locale("C"));
        ev_test data(state.range_x());

        while (state.KeepRunning())
        {
                boost_test<boost_binary_trait>::to_wire(data);
        }

        state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
                state.range_x());
}
BENCHMARK(to_wire_binary)->Range(8, range_mult << range_max_step);

static void from_wire_binary(benchmark::State &state)
{
        std::locale::global(std::locale("C"));

        auto buffer =
                boost_test<boost_binary_trait>::to_wire(ev_test(state.range_x()));

        while (state.KeepRunning())
        {
                boost_test<boost_binary_trait>::from_wire(buffer);
        }

        state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) *
                state.range_x());
}
BENCHMARK(from_wire_binary)->Range(8, range_mult << range_max_step);

BENCHMARK_MAIN();
</code>


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net