其他分享
首页 > 其他分享> > c – 将boost :: iostreams :: mapped_file_source与std :: multimap一起使用

c – 将boost :: iostreams :: mapped_file_source与std :: multimap一起使用

作者:互联网

我有相当多的数据要分析 – 每个文件大约5gig.每个文件都具有以下格式:

xxxxx yyyyy

键和值都可以重复,但键按递增顺序排序.我正在尝试使用内存映射文件来实现此目的,然后找到所需的密钥并使用它们.这就是我写的:

if (data_file != "")
{
    clock_start = clock();
    data_file_mapped.open(data_file);

    data_multimap = (std::multimap<double, unsigned int> *)data_file_mapped.data();
    if (data_multimap != NULL)
    {
        std::multimap<double, unsigned int>::iterator it = data_multimap->find(keys_to_find[4]);
        if (it != data_multimap->end())
        {
            std::cout << "Element found.";
            for (std::multimap<double, unsigned int>::iterator it = data_multimap->lower_bound(keys_to_find[4]); it != data_multimap->upper_bound(keys_to_find[5]); ++it)
            {
                std::cout << it->second;
            }
            std::cout << "\n";
            clock_end = clock();

            std::cout << "Time taken to read in the file: " << (clock_end - clock_start)/CLOCKS_PER_SEC << "\n";
        }
        else
            std::cerr << "Element not found at all" << "\n";
    }
    else
        std::cerr << "Nope - no data received."<< "\n";
}

基本上,我需要找到键的范围并拉出那些块来处理.我第一次尝试在multimap上使用方法时遇到段错误.例如,调用find方法时.我也试过了upper_bound,lower_bound和其他方法,但仍然遇到了段错误.

这就是gdb给我的:

Program received signal SIGSEGV, Segmentation fault.
_M_lower_bound (this=<optimized out>, __k=<optimized out>, __y=<optimized out>, __x=0xa31202030303833) at /usr/include/c++/4.9.2/bits/stl_tree.h:1261
1261            if (!_M_impl._M_key_compare(_S_key(__x), __k))

有人可以指出我做错了什么吗?我只能在内存映射文件上找到简单的例子 – 就像这样.谢谢.

编辑:更多信息:

我上面描述的文件基本上是一个两列纯文本文件,神经模拟器给我作为我模拟的输出.它很简单:

$du -hsc 201501271755.e.ras 
4.9G    201501271755.e.ras
4.9G    total
$head 201501271755.e.ras 
0.013800  0
0.013800  1
0.013800  10
0.013800  11
0.013800  12
0.013800  13
0.013800  14
0.013800  15
0.013800  16
0.013800  17

第一列是时间,第二列是此时发射的神经元 – (它是一个尖峰时间光栅文件).实际上,输出是来自用于运行模拟的每个MPI等级的这样的文件.已使用sort -g -m将各种文件合并到此主文件中.有关文件格式的更多信息,请访问:http://www.fzenke.net/auryn/doku.php?id=manual:ras

为了计算在模拟的某些时间设置的神经元的发射率和其他度量,我需要 – 在文件中定位时间,在[时间-1,时间]之间拉出一个块并运行一些指标等等这个块.这个文件非常小,我希望随着模拟变得越来越复杂并且运行时间越来越长,尺寸会增加很多.这就是我开始研究内存映射文件的原因.我希望在某种程度上澄清问题陈述.我只需要读取输出文件来处理它包含的信息.我根本不需要修改这个文件.

为了处理数据,我将再次使用多个线程,但由于我在地图上的所有操作都是只读的,所以我不希望在那里遇到麻烦.

解决方法:

你正在尝试的东西你真的不明白:)没问题.

多个映射不按顺序排列在内存中. (它们是基于节点的容器,但我离题了).事实上,即使它们是,但布局与文本输入的布局相匹配的可能性很小.

基本上有两种方法可以使这项工作:

>继续使用multimap但使用自定义分配器(以便所有分配都在映射的内存区域中完成).这是来自高级C视点的“最好”,/但是/您需要更改为文件的二进制格式.

如果可以的话,这就是我的建议. Boost Container Boost Interprocess拥有您需要的一切,使其相对无痛.
>您编写了一个直接在映射数据上工作的自定义容器“抽象”.你可以

>从任何地方识别“xxxx yyyy”对(行尾?)或
>在文件中构建所有行开头的索引.

使用这些可以设计一个可用于实现更高级别操作(lower_bound,upper_bound和equal_range)的Interator(Boost Iterator iterator_facade).

一旦你有了这些,你基本上都设置为查询这个内存映射作为只读键值数据库.

遗憾的是,如果您还想支持变异操作(插入,删除),这种内存表示对于性能会非常糟糕.

如果您有该文件的实际样本,我可以演示所描述的任何一种方法.

更新

快速样品:

>使用boost :: interprocess,您可以(非常)简单地定义您想要的多图:

namespace shared {
    namespace bc = boost::container;

    template <typename T> using allocator = bip::allocator<T, bip::managed_mapped_file::segment_manager>;
    template <typename K, typename V>
        using multi_map = bc::flat_multimap<
            K, V, std::less<K>, 
            allocator<typename bc::flat_multimap<K, V>::value_type> >;
}

笔记:

>我选择了flatmap(实际上是flat_multimap),因为它可能更多
存储效率高,与第二种方法更具可比性
(见下文);

请注意,此选择会影响迭代器/参考稳定性
非常喜欢只读操作.如果你需要迭代器
稳定性和/或许多变异操作,使用常规地图(或用于
非常高的一个hash_map而不是平面变化.
>我为此演示选择了一个managed_mapped_file段(因此您获得了持久性).该演示展示了10G是如何稀疏预分配的,但是只在磁盘上使用了实际分配的空间.你也可以使用managed_shared_memory.

如果您有二进制持久性,则可以完全丢弃文本数据文件.
>我将文本数据解析为shared :: multi_map< double,unsigned>来自使用Boost Spirit的mapped_file_source.实现完全通用.
>不需要编写迭代器类,start_of_line(),end_of_line(),lower_bound(),upper_bound(),equal_range()或其中任何一个,因为它们已经是multi_map接口的标准,所以我们需要写主要:

Live On Coliru

#define NDEBUG
#undef DEBUG
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace bip = boost::interprocess;
namespace qi = boost::spirit::qi;

namespace shared {
    namespace bc = boost::container;

    template <typename T> using allocator = bip::allocator<T, bip::managed_mapped_file::segment_manager>;
    template <typename K, typename V>
        using multi_map = bc::flat_multimap<
            K, V, std::less<K>, 
            allocator<typename bc::flat_multimap<K, V>::value_type> >;
}

#include <iostream>

bip::managed_mapped_file msm(bip::open_or_create, "lookup.bin", 10ul<<30);

template <typename K, typename V>
shared::multi_map<K,V>& get_or_load(const char* fname) {
    using Map = shared::multi_map<K, V>;
    Map* lookup = msm.find_or_construct<Map>("lookup")(msm.get_segment_manager());

    if (lookup->empty()) { 
        // only read input file if not already loaded
        boost::iostreams::mapped_file_source input(fname);
        auto f(input.data()), l(f + input.size());

        bool ok = qi::phrase_parse(f, l,
                (qi::auto_ >> qi::auto_) % qi::eol >> *qi::eol, 
                qi::blank, *lookup);

        if (!ok || (f!=l))
            throw std::runtime_error("Error during parsing at position #" + std::to_string(f - input.data()));
    }

    return *lookup;
}

int main() {
    // parse text file into shared memory binary representation
    auto const& lookup = get_or_load<double, unsigned int>("input.txt");
    auto const e = lookup.end();

    for(auto&& line : lookup)
    {
        std::cout << line.first << "\t" << line.second << "\n";

        auto er = lookup.equal_range(line.first);

        if (er.first  != e) std::cout << " lower: " << er.first->first  << "\t" << er.first->second  << "\n";
        if (er.second != e) std::cout << " upper: " << er.second->first << "\t" << er.second->second << "\n";
    }
}

>我完全按照我的描述实现了它:

>简单容器覆盖原始const char *区域;
>使用boost :: iterator_facade创建一个迭代器来解析dereference上的文本;
>为了打印输入行,我使用boost :: string_ref – 这避免了复制字符串的动态分配.
>使用Spirit Qi完成解析:

if (!qi::phrase_parse(
            b, _data.end,
            qi::auto_ >> qi::auto_ >> qi::eoi,
            qi::space,
            _data.key, _data.value)) 

选择Qi是为了提高速度和通用性:您可以在实例化时选择Key和Value类型:

text_multi_lookup<double, unsigned int> tml(map.data(), map.data() + map.size());

>我已经实现了利用底层连续存储的lower_bound,upper_bound和equal_range成员函数.即使“行”迭代器不是随机访问而是双向,我们仍然可以跳转到这样的迭代器范围的mid_point,因为我们可以从任何const char *获取start_of_line到底层映射区域.这使二进制搜索有效.

请注意,此解决方案在迭代器的解引用上解析行.如果相同的行被解除引用很多次,这可能效率不高.

但是,对于不频繁的查找或在输入数据的同一区域中不常见的查找,这几乎和它可能获得的效率一样高(仅进行最少的所需解析和O(log n)二进制搜索),通过映射文件完全绕过初始加载时间(没有访问意味着不需要加载任何东西).

Live On Coliru(包括测试数据)

#define NDEBUG
#undef DEBUG
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/include/qi.hpp>
#include <thread>
#include <iomanip>

namespace io = boost::iostreams;
namespace qi = boost::spirit::qi;

template <typename Key, typename Value> 
struct text_multi_lookup {
    text_multi_lookup(char const* begin, char const* end)
        : _map_begin(begin), 
          _map_end(end)
    {
    }

  private:
    friend struct iterator;
    enum : char { nl = '\n' };

    using rawit = char const*;
    rawit _map_begin, _map_end;

    rawit start_of_line(rawit it) const {
        while (it > _map_begin) if (*--it == nl) return it+1;
        assert(it == _map_begin);
        return it;
    }

    rawit end_of_line(rawit it) const {
        while (it < _map_end) if (*it++ == nl) return it;
        assert(it == _map_end);
        return it;
    }

  public:
    struct value_type final {
        rawit beg, end;
        Key   key;
        Value value;

        boost::string_ref str() const { return { beg, size_t(end-beg) }; }
    };

    struct iterator : boost::iterator_facade<iterator, boost::string_ref, boost::bidirectional_traversal_tag, value_type> {

        iterator(text_multi_lookup const& d, rawit it) : _region(&d), _data { it, nullptr, Key{}, Value{} } { 
            assert(_data.beg == _region->start_of_line(_data.beg));
        }

      private:
        friend text_multi_lookup;

        text_multi_lookup const* _region;
        value_type mutable _data;

        void ensure_parsed() const {
            if (!_data.end) 
            {
                assert(_data.beg == _region->start_of_line(_data.beg));
                auto b = _data.beg;
                _data.end = _region->end_of_line(_data.beg);

                if (!qi::phrase_parse(
                            b, _data.end,
                            qi::auto_ >> qi::auto_ >> qi::eoi,
                            qi::space,
                            _data.key, _data.value)) 
                {
                    std::cerr << "Problem in: " << std::string(_data.beg, _data.end) 
                              << "at:         " << std::setw(_data.end-_data.beg) << std::right << std::string(_data.beg,_data.end);
                    assert(false);
                }
            }
        }

        static iterator mid_point(iterator const& a, iterator const& b) {
            assert(a._region == b._region);
            return { *a._region, a._region->start_of_line(a._data.beg + (b._data.beg -a._data.beg)/2) };
        }

      public:
        value_type const& dereference() const {
            ensure_parsed();
            return _data;
        }

        bool equal(iterator const& o) const {
            return (_region == o._region) && (_data.beg == o._data.beg);
        }

        void increment() {
            _data = { _region->end_of_line(_data.beg), nullptr, Key{}, Value{} };
            assert(_data.beg == _region->start_of_line(_data.beg));
        }
    };

    using const_iterator = iterator;

    const_iterator begin()  const { return { *this, _map_begin }; }
    const_iterator end()    const { return { *this, _map_end   }; }
    const_iterator cbegin() const { return { *this, _map_begin }; }
    const_iterator cend()   const { return { *this, _map_end   }; }

    template <typename CompatibleKey>
    const_iterator lower_bound(CompatibleKey const& key) const {
        auto f(begin()), l(end());
        while (f!=l) {
            auto m = iterator::mid_point(f,l);

            if (m->key < key) {
                f = m;
                ++f;
            }
            else {
                l = m;
            }
        }
        return f;
    }

    template <typename CompatibleKey>
    const_iterator upper_bound(CompatibleKey const& key) const {
        return upper_bound(key, begin());
    }

  private:
    template <typename CompatibleKey>
    const_iterator upper_bound(CompatibleKey const& key, const_iterator f) const {
        auto l(end());
        while (f!=l) {
            auto m = iterator::mid_point(f,l);

            if (key < m->key) {
                l = m;
            }
            else {
                f = m;
                ++f;
            }
        }
        return f;
    }

  public:
    template <typename CompatibleKey>
    std::pair<const_iterator, const_iterator> equal_range(CompatibleKey const& key) const {
        auto lb = lower_bound(key);
        return { lb, upper_bound(key, lb) };
    }

};

#include <iostream>

int main() {
    io::mapped_file_source map("input.txt");
    text_multi_lookup<double, unsigned int> tml(map.data(), map.data() + map.size());

    auto const e = tml.end();

    for(auto&& line : tml)
    {
        std::cout << line.str();

        auto er = tml.equal_range(line.key);

        if (er.first  != e) std::cout << " lower: " << er.first->str();
        if (er.second != e) std::cout << " upper: " << er.second->str();
    }
}

对于好奇:这是拆卸.请注意所有算法内容如何直接内联到main:http://paste.ubuntu.com/9946135/

标签:multimap,memory-mapped-files,c,boost
来源: https://codeday.me/bug/20190926/1819758.html