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

时间:2015-01-29 14:36:24

标签: c++ boost memory-mapped-files 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_boundlower_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,时间]之间拉出一个块并运行一些指标和等等这个块。这个文件非常小,我希望随着模拟变得越来越复杂并且运行时间越来越长,尺寸会增加很多。这就是我开始研究内存映射文件的原因。我希望在某种程度上澄清问题陈述。我只需要读取输出文件来处理它包含的信息。我根本不需要修改这个文件。

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

2 个答案:

答案 0 :(得分:4)

你正在尝试一些你并不理解的事情:)没问题。

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

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

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

    如果可以的话,这就是我的建议。 Boost Container + Boost Interprocess拥有您需要的一切,使其相对无痛。

  2. 您编写了一个直接在映射数据上工作的自定义容器“抽象”。你可以

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

    使用这些可以设计一个可用于实现更高级别操作(iterator_facadelower_boundupper_bound)的交互者(Boost Iterator equal_range)。

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

    可悲的是,如果你还想支持变异操作(insertremove),这种内存表示对于性能来说非常

    < / LI>

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

    更新

    快速样本:

    1. 使用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> >;
      }
      

      注意:

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

        请注意,此选择会影响迭代器/引用的稳定性 非常喜欢只读操作。如果你需要迭代器 稳定性和/或许多变异操作,使用常规map(或用于 非常高的量hash_map)而不是 flat 变体。

      • 我为此演示选择了managed_mapped_file段(因此您获得了持久性)。该演示展示了10G是如何稀疏预分配的,但实际分配的空间仅在磁盘上使用。你也可以使用managed_shared_memory

        如果您有二进制持久性,可能完全丢弃文本数据文件。

      • 我使用Boost Spirit将文本数据解析为来自shared::multi_map<double, unsigned>的{​​{1}}。实施完全通用。

      • 无需撰写mapped_file_source个课程,iteratorstart_of_line()end_of_line()lower_bound()upper_bound()或任何其中,因为它们已经成为equal_range()界面的标准,所以我们只需要写multi_map

      <强> Live On Coliru

      main
    2. 我完全按照我的描述实现了它:

      • 映射的原始#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的输入行 - 这可以避免动态分配复制字符串。
      • 使用Spirit Qi完成解析:

        boost::string_ref

        选择Qi是为了提高速度和通用性:您可以在实例化时选择if (!qi::phrase_parse( b, _data.end, qi::auto_ >> qi::auto_ >> qi::eoi, qi::space, _data.key, _data.value)) Key类型:

        Value
      • 我已经实现了利用基础连续存储的text_multi_lookup<double, unsigned int> tml(map.data(), map.data() + map.size()); lower_boundupper_bound成员函数。即使“行”equal_range不是随机访问而是双向的,我们仍然可以跳转到这样的迭代器范围的iterator,因为我们可以从任何{{1}获得mid_point进入底层映射区域。这使得二进制搜索更有效。

      请注意,此解决方案会解析start_of_line的取消引用行。如果相同的行被解除引用很多次,这可能效率不高。

      但是,对于不常见的查找或在输入数据的同一区域中不常见的查找,这几乎和它可能获得的效率一样高(仅进行最少的必需解析和const char*二进制搜索),一直完全通过映射文件来绕过初始加载时间(没有访问意味着什么都不需要加载)。

      Live On Coliru (包括测试数据)

      iterator

      对于好奇:这是拆卸。请注意所有算法内容如何内嵌到O(log n)http://paste.ubuntu.com/9946135/

答案 1 :(得分:1)

data_multimap = (std::multimap<double, unsigned int> *)data_file_mapped.data();,到目前为止,我可以从boost documentation读取,您已经错过了该功能,该转换将无效,您需要使用{{1提供的字符*填充多图}}

我编辑添加更详细的内容,例如在映射之后,你可以做

data()

然后,在线上传送内容(您可以使用字符串流执行该任务)并填充多图。

重复此过程,直到文件结束。