From e9d33b0a1f3ffffac628754dc93c7670a39d4662 Mon Sep 17 00:00:00 2001 From: Shane Grant Date: Fri, 5 Feb 2016 10:14:44 -0800 Subject: [PATCH] BinaryData optimization for Bitset Integrates the changes proposed by #236 Bitset will now use a chunking method when serializing to BinaryData capable archives, which should be faster, especially for larger bitsets. Archives that do not support BinaryData will continue to use the old method. Also added a larger bitset to the test case --- include/cereal/types/bitset.hpp | 67 +++++++++++++++++++++++++++++++-- unittests/bitset.cpp | 6 +++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/include/cereal/types/bitset.hpp b/include/cereal/types/bitset.hpp index d421e1088..57836734b 100644 --- a/include/cereal/types/bitset.hpp +++ b/include/cereal/types/bitset.hpp @@ -43,12 +43,49 @@ namespace cereal { ulong, ullong, - string + string, + bits }; } - //! Serializing (save) for std::bitset - template inline + //! Serializing (save) for std::bitset when BinaryData optimization supported + template , Archive>::value> + = traits::sfinae> inline + void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::bitset const & bits ) + { + ar( CEREAL_NVP_("type", bitset_detail::type::bits) ); + + // Serialize 8 bit chunks + std::uint8_t chunk = 0; + std::uint8_t mask = 0x80; + + // Set each chunk using a rotating mask for the current bit + for( std::size_t i = 0; i < N; ++i ) + { + if( bits[i] ) + chunk |= mask; + + mask >>= 1; + + // output current chunk when mask is empty (8 bits) + if( mask == 0 ) + { + ar( chunk ); + chunk = 0; + mask = 0x80; + } + } + + // serialize remainder, if it exists + if( mask != 0x80 ) + ar( chunk ); + } + + //! Serializing (save) for std::bitset when BinaryData is not supported + template , Archive>::value> + = traits::sfinae> inline void CEREAL_SAVE_FUNCTION_NAME( Archive & ar, std::bitset const & bits ) { try @@ -103,6 +140,30 @@ namespace cereal bits = std::bitset( b ); break; } + case bitset_detail::type::bits: + { + // Normally we would use BinaryData to route this at compile time, + // but doing this at runtime doesn't break any old serialization + std::uint8_t chunk = 0; + std::uint8_t mask = 0; + + // Load one chunk at a time, rotating through the chunk + // to set bits in the bitset + for( std::size_t i = 0; i < N; ++i ) + { + if( mask == 0 ) + { + ar( chunk ); + mask = 0x80; + } + + if( chunk & mask ) + bits[i] = 1; + + mask >>= 1; + } + break; + } default: throw Exception("Invalid bitset data representation"); } diff --git a/unittests/bitset.cpp b/unittests/bitset.cpp index 51765315c..d882fdb71 100644 --- a/unittests/bitset.cpp +++ b/unittests/bitset.cpp @@ -36,12 +36,14 @@ void test_bitset() auto rng32 = [&](){ return random_binary_string<32>( gen ); }; auto rng65 = [&](){ return random_binary_string<65>( gen ); }; auto rng256 = [&](){ return random_binary_string<256>( gen ); }; + auto rng512 = [&](){ return random_binary_string<512>( gen ); }; for(int ii=0; ii<100; ++ii) { std::bitset<32> o_bit32( rng32() ); std::bitset<65> o_bit65( rng65() ); std::bitset<256> o_bit256( rng256() ); + std::bitset<512> o_bit512( rng512() ); std::ostringstream os; { @@ -50,11 +52,13 @@ void test_bitset() oar(o_bit32); oar(o_bit65); oar(o_bit256); + oar(o_bit512); } std::bitset<32> i_bit32; std::bitset<65> i_bit65; std::bitset<256> i_bit256; + std::bitset<512> i_bit512; std::istringstream is(os.str()); { @@ -63,11 +67,13 @@ void test_bitset() iar(i_bit32); iar(i_bit65); iar(i_bit256); + iar(i_bit512); } BOOST_CHECK_EQUAL( o_bit32, i_bit32 ); BOOST_CHECK_EQUAL( o_bit65, i_bit65 ); BOOST_CHECK_EQUAL( o_bit256, i_bit256 ); + BOOST_CHECK_EQUAL( o_bit512, i_bit512 ); } }