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接口的标准,所以我们需要写主要:
#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