我需要分析相当多的数据 - 每个文件大约是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,时间]之间拉出一个块并运行一些指标和等等这个块。这个文件非常小,我希望随着模拟变得越来越复杂并且运行时间越来越长,尺寸会增加很多。这就是我开始研究内存映射文件的原因。我希望在某种程度上澄清问题陈述。我只需要读取输出文件来处理它包含的信息。我根本不需要修改这个文件。
为了处理数据,我将再次使用多个线程,但由于我在地图上的所有操作都是只读的,所以我不希望在那里遇到麻烦。
答案 0 :(得分:4)
你正在尝试一些你并不理解的事情:)没问题。
多张地图不按顺序排列在内存中。 (它们是基于节点的容器,但我离题了)。事实上,即使它们是,但布局与文本输入的布局相匹配的可能性很小。
基本上有两种方法可以使这项工作:
继续使用multimap
但使用自定义分配器(以便所有分配都在映射的内存区域中完成)。这是来自高级C ++观点的“最好的”,/但是/您需要更改为文件的二进制格式。
如果可以的话,这就是我的建议。 Boost Container + Boost Interprocess拥有您需要的一切,使其相对无痛。
您编写了一个直接在映射数据上工作的自定义容器“抽象”。你可以
使用这些可以设计一个可用于实现更高级别操作(iterator_facade
,lower_bound
和upper_bound
)的交互者(Boost Iterator equal_range
)。
一旦你有了这些,你基本上都设置为查询这个内存映射作为只读键值数据库。
可悲的是,如果你还想支持变异操作(insert
,remove
),这种内存表示对于性能来说非常。
如果您有该文件的实际样本,我可以演示所描述的任何一种方法。
快速样本:
使用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
,实际上)因为它可能更多
存储效率高,与第二种方法更具可比性
(见下文);
请注意,此选择会影响迭代器/引用的稳定性
非常喜欢只读操作。如果你需要迭代器
稳定性和/或许多变异操作,使用常规map
(或用于
非常高的量hash_map
)而不是 flat 变体。
我为此演示选择了managed_mapped_file
段(因此您获得了持久性)。该演示展示了10G是如何稀疏预分配的,但实际分配的空间仅在磁盘上使用。你也可以使用managed_shared_memory
。
如果您有二进制持久性,可能完全丢弃文本数据文件。
我使用Boost Spirit将文本数据解析为来自shared::multi_map<double, unsigned>
的{{1}}。实施完全通用。
无需撰写mapped_file_source
个课程,iterator
,start_of_line()
,end_of_line()
,lower_bound()
,upper_bound()
或任何其中,因为它们已经成为equal_range()
界面的标准,所以我们只需要写multi_map
:
<强> Live On Coliru 强>
main
我完全按照我的描述实现了它:
#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_bound
和upper_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()
然后,在线上传送内容(您可以使用字符串流执行该任务)并填充多图。
重复此过程,直到文件结束。