2015-05-03 1 views
2

это моя программа:Почему пространство над головой при десериализации из бинарного архива в станд :: Карта

void loadB(map<unsigned int,myParam> & myParams) 
{ 
    std::ifstream ifs("/tmp/all_params", std::ios::in | std::ios::binary); 
    if(ifs.good()){ 
     try{ 
      boost::archive::binary_iarchive ia(ifs); 
      ia >> myParams; 

      ifs.close(); 
     }catch(boost::archive::archive_exception& ex){ 
      syslog(LOG_NOTICE, "Archive Exception during deserializing params"); 
     } 
    }else{ } 
} 

Размер файла «/ TMP/all_params» является 133M, но когда я загрузить его с функцией loadB() потребление памяти составляет более 650M (1.7G виртуальный). Это имеет смысл?

PID USER  PR NI VIRT RES SHR S %CPU %MEM  TIME+ COMMAND 
16619 root  20 0 1767468 653772 2988 S 3.7 8.0 0:06.21 engine                                   
+4

В пещеру SSCCE ... –

+0

Да. Возможно, есть утечка памяти без Boost Serialization. Это позволяет легко иметь память _with_ it. – sehe

+0

Ах. Вопрос в том, что речь не идет об утечке памяти. Это была красная сельдь. Возникает вопрос о (потенциальной) неэффективности стандартных распределителей кучи. – sehe

ответ

1

Конечно, это имеет смысл.

E.g. когда /tmp/all_params файл генерируется со следующей программой:

Live On Coliru

#include <boost/serialization/map.hpp> 
#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 
#include <boost/random.hpp> 
#include <boost/bind.hpp> 

struct myParam { 
    std::string data; 
    template <typename Ar> void serialize(Ar& ar, unsigned) { 
     ar & data; 
    } 
}; 

static inline std::string generate_value() { 
    static auto rand_char = boost::bind(boost::uniform_int<unsigned char>(0,255), boost::mt19937{}); 

    std::string s; 
    std::generate_n(back_inserter(s), rand_char(), rand_char); 
    return s; 
} 

using Map = std::map<unsigned int,myParam>; 

Map generate_data(unsigned n) { 
    Map map; 

    for (unsigned i=0; i<n; ++i) 
     map.emplace(i, myParam { generate_value() }); 

    return map; 
} 

#include <fstream> 
#include <iostream> 

int main() { 
    { 
     std::ofstream ofs("/tmp/all_params", std::ios::binary); 
     boost::archive::binary_oarchive oa(ofs); 

     auto data = generate_data(10ul<<19); 
     oa << data; 
     std::cout << "Serialized " << data.size() << " entries\n"; 
    } 
} 

Файл был 698miB на моей системе. Объем памяти выглядит следующим образом (требуется некоторое время :)

==27420== Memcheck, a memory error detector 
==27420== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. 
==27420== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info 
==27420== Command: ./test 
==27420== 
Serialized 5242880 entries 
==27420== 
==27420== HEAP SUMMARY: 
==27420==  in use at exit: 0 bytes in 0 blocks 
==27420== total heap usage: 47,021,247 allocs, 47,021,247 frees, 3,069,877,283 bytes allocated 
==27420== 
==27420== All heap blocks were freed -- no leaks are possible 
==27420== 

снимок использования пик был на 1,2 ГиБ:

enter image description here

Конечно, вы можете оптимизировать расположение памяти, например, используя Boost Flat Map (с перегрузочными вставками ordered_unique_range_t!) и пользовательский распределитель, например. строки там. Это позволит сократить/устранить накладные расходы:

enter image description here

подправленный код:

#include <boost/serialization/map.hpp> 
#include <boost/serialization/collections_load_imp.hpp> 
#include <boost/serialization/collections_save_imp.hpp> 
#include <boost/container/flat_map.hpp> 
#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 
#include <boost/random.hpp> 
#include <boost/bind.hpp> 
#include <boost/utility/string_ref.hpp> 
#include <cassert> 

namespace string_pool { 
    static auto pool = []{ 
     std::vector<char> init; 
     init.reserve(700ul<<20); // 700MiB 
     return init; 
    }(); 

    using entry = boost::string_ref; 

    entry add(std::string const& s) { 
     assert((pool.capacity() >= (pool.size() + s.size()))); 

     auto it = pool.end(); 
     pool.insert(it, s.begin(), s.end()); 
     return { &*it, s.size() }; 
    } 

    static inline entry generate_random() { 
     static auto rand_char = boost::bind(boost::uniform_int<unsigned char>(0,255), boost::mt19937{}); 

     static std::string s; // non-reentrant, but for lazy demo 
     s.resize(rand_char()); 
     std::generate_n(s.begin(), s.size(), rand_char); 
     return add(s); 
    } 
} 

struct myParam { 
    string_pool::entry data; 

    template <typename Ar> void save(Ar& ar, unsigned) const { 
     std::string s = data.to_string(); 
     ar & s; 
    } 
    template <typename Ar> void load(Ar& ar, unsigned) { 
     std::string s; 
     ar & s; 
     data = string_pool::add(s); 
    } 
    BOOST_SERIALIZATION_SPLIT_MEMBER() 
}; 

// flat map serialization 
namespace boost { 
namespace serialization { 

    template<class Archive, typename...TArgs> 
    inline void save(
     Archive & ar, 
     const boost::container::flat_map<TArgs...> &t, 
     const unsigned int /* file_version */ 
    ){ 
     boost::serialization::stl::save_collection< 
      Archive, 
      boost::container::flat_map<TArgs...> 
     >(ar, t); 
    } 

    template<class Archive, typename...TArgs> 
    inline void load(Archive & ar, boost::container::flat_map<TArgs...> &t, const unsigned int /* file_version */) { 
     boost::serialization::stl::load_collection<Archive, boost::container::flat_map<TArgs...>, 
      boost::serialization::stl::archive_input_map<Archive, boost::container::flat_map<TArgs...> >, 
      boost::serialization::stl::reserve_imp <boost::container::flat_map<TArgs...> > 
     >(ar, t); 
    } 

    // split non-intrusive serialization function member into separate 
    // non intrusive save/load member functions 
    template<class Archive, typename...TArgs> 
    inline void serialize(Archive & ar, boost::container::flat_map<TArgs...> &t, const unsigned int file_version) { 
     boost::serialization::split_free(ar, t, file_version); 
    } 
} 
} 

using Map = boost::container::flat_map<unsigned int,myParam>; 

Map generate_data(unsigned n) { 
    Map map; 
    map.reserve(n); 
    std::cout << "Capacity: " << map.capacity() << "\n"; 

    for (unsigned i=0; i<n; ++i) 
     map.emplace(i, myParam { string_pool::generate_random() }); 

    std::cout << "Capacity: " << map.capacity() << "\n"; 
    std::cout << "Total length: " << std::accumulate(
      map.begin(), map.end(), 0ul, [](size_t acc, Map::value_type const& v) { 
       return acc + v.second.data.size(); 
      }) << "\n"; 
    return map; 
} 

#include <fstream> 
#include <iostream> 

int main() { 
    { 
     std::ofstream ofs("/tmp/all_params", std::ios::binary); 
     boost::archive::binary_oarchive oa(ofs); 

     auto data = generate_data(10ul<<19); 
     oa << data; 
     std::cout << "Serialized " << data.size() << " entries\n"; 
    } 
} 

md5sum генерируемого /tmp/all_params файла соответствует, что в первой версии: ac75521dc0dc65585368677c834613cb, доказывающие, что данные сериализованы является на самом деле то же самое.

+0

Предоставлена ​​измененная версия с использованием 'boost :: string_ref' и' boost :: container :: flat_map', которая показывает ** 32% уменьшенного объема памяти ** при правильном резервировании спереди. Вы бы хотели получить более разумный пул строк, но это должно быть хорошей демонстрацией, чтобы ответить на ваш вопрос. – sehe

Смежные вопросы