Эффективный анализ файла mmap

Ниже приведен код для создания файла карты памяти с использованием boost.

boost::iostreams::mapped_file_source file;
boost::iostreams::mapped_file_params param;
param.path = "\\..\\points.pts";  //! Filepath
file.open(param, fileSize);
if(file.is_open())
{
//! Access the buffer and populate the ren point buffer
const char* pData = file.data();
char* pData1 = const_cast<char*>(pData);  //! this gives me all the data from Mmap file
std::vector<RenPoint> readPoints;
ParseData( pData1, readPoints);
}

Реализация ParseData заключается в следующем

void ParseData ( char* pbuffer , std::vector<RenPoint>>& readPoints)
{
if(!pbuffer)
throw std::logic_error("no Data in memory mapped file");

stringstream strBuffer;
strBuffer << pbuffer;

//! Get the max number of points in the pts file
std::string strMaxPts;
std::getline(strBuffer,strMaxPts,'\n');
auto nSize = strMaxPts.size();
unsigned nMaxNumPts = GetValue<unsigned>(strMaxPts);
readPoints.clear();

//! Offset buffer
pbuffer += nSize;
strBuffer << pbuffer;
std::string cur_line;
while(std::getline(strBuffer, cur_line,'\n'))
{
//! How do I read the data from mmap file directly and populate my renpoint structure
int yy = 0;
}

//! Working but very slow
/*while (std::getline(strBuffer,strMaxPts,'\n'))
{
std::vector<string> fragments;

istringstream iss(strMaxPts);

copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter<vector<string>>(fragments));

//! Logic to populate the structure after getting data back from fragments
readPoints.push_back(pt);
}*/
}

Я сказал, что минимум 1 миллион точек в моей структуре данных, и я хочу оптимизировать мой анализ. Есть идеи ?

2

Решение

  1. прочитайте в заголовке информацию, чтобы получить количество баллов
  2. зарезервировать пространство в std :: vector для N * num_points (N = 3, предполагая только X, Y, Z, 6 с нормалями, 9 с нормалями и rgb)
  3. загрузить оставшуюся часть файла в строку
  4. boost :: spirit :: qi :: фразу_parse в вектор.

// код может проанализировать файл с 40M точками (> 1 ГБ) примерно за 14 секунд на моем 2-летнем MacBook:

#include <boost/spirit/include/qi.hpp>
#include <fstream>
#include <vector>

template <typename Iter>
bool parse_into_vec(Iter p_it, Iter p_end, std::vector<float>& vf) {
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::float_;
using boost::spirit::qi::ascii::space;

bool ret = phrase_parse(p_it, p_end, *float_, space, vf);
return p_it != p_end ? false : ret;
}

int main(int argc, char **args) {
if(argc < 2) {
std::cerr << "need a file" << std::endl;
return -1;
}
std::ifstream in(args[1]);

size_t numPoints;
in >> numPoints;

std::istreambuf_iterator<char> eos;
std::istreambuf_iterator<char> it(in);
std::string strver(it, eos);

std::vector<float> vf;
vf.reserve(3 * numPoints);

if(!parse_into_vec(strver.begin(), strver.end(), vf)) {
std::cerr << "failed during parsing" << std::endl;
return -1;
}

return 0;
}
2

Другие решения

AFAICT, вы сейчас копируете все содержимое файла в strBuffer,

Я думаю, что вы хотите сделать, это использовать boost::iostreams::stream с вашим mapped_file_source вместо.

Вот непроверенный пример, основанный на связанной документации:

// Create the stream
boost::iostreams::stream<boost::iostreams::mapped_file_source> str("some/path/file");
// Alternately, you can create the mapped_file_source separately and tell the stream to open it (using a copy of your mapped_file_source)
boost::iostreams::stream<boost::iostreams::mapped_file_source> str2;
str2.open(file);

// Now you can use std::getline as you normally would.
std::getline(str, strMaxPts);

В качестве отступления отмечу, что по умолчанию mapped_file_source сопоставляет весь файл, поэтому нет необходимости явно передавать размер.

1

Вы можете пойти с чем-то вроде этого (просто быстрая концепция, вам нужно добавить дополнительную проверку ошибок и т. Д.):

#include "boost/iostreams/stream.hpp"#include "boost/iostreams/device/mapped_file.hpp"#include "boost/filesystem.hpp"#include "boost/lexical_cast.hpp"
double parse_double(const std::string & str)
{
double value = 0;
bool decimal = false;
double divisor = 1.0;
for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
{
switch (*it)
{
case '.':
case ',':
decimal = true;
break;
default:
{
const int x = *it - '0';
value = value * 10 + x;
if (decimal)
divisor *= 10;
}
break;
}
}
return value / divisor;
}void process_value(const bool initialized, const std::string & str, std::vector< double > & values)
{
if (!initialized)
{
// convert the value count and prepare the output vector
const size_t count = boost::lexical_cast< size_t >(str);
values.reserve(count);
}
else
{
// convert the value
//const double value = 0; // ~ 0:20 min
const double value = parse_double(str); // ~ 0:35 min
//const double value = atof(str.c_str()); // ~ 1:20 min
//const double value = boost::lexical_cast< double >(str); // ~ 8:00 min ?!?!?
values.push_back(value);
}
}bool load_file(const std::string & name, std::vector< double > & values)
{
const int granularity = boost::iostreams::mapped_file_source::alignment();
const boost::uintmax_t chunk_size = ( (256 /* MB */ << 20 ) / granularity ) * granularity;
boost::iostreams::mapped_file_params in_params(name);
in_params.offset = 0;
boost::uintmax_t left = boost::filesystem::file_size(name);
std::string value;
bool whitespace = true;
bool initialized = false;
while (left > 0)
{
in_params.length = static_cast< size_t >(std::min(chunk_size, left));
boost::iostreams::mapped_file_source in(in_params);
if (!in.is_open())
return false;
const boost::iostreams::mapped_file_source::size_type size = in.size();
const char * data = in.data();
for (boost::iostreams::mapped_file_source::size_type i = 0; i < size; ++i, ++data)
{
const char c = *data;
if (strchr(" \t\n\r", c))
{
// c is whitespace
if (!whitespace)
{
whitespace = true;
// finished previous value
process_value(initialized, value, values);
initialized = true;
// start a new value
value.clear();
}
}
else
{
// c is not whitespace
whitespace = false;
// append the char to the value
value += c;
}
}
if (size < chunk_size)
break;
in_params.offset += chunk_size;
left -= chunk_size;
}
if (!whitespace)
{
// convert the last value
process_value(initialized, value, values);
}
return true;
}

Обратите внимание, что вашей основной проблемой будет преобразование строки в число с плавающей точкой, которое очень медленное (безумно медленное в случае boost :: lexical_cast). С моей специальной функцией parse_double она работает быстрее, однако позволяет использовать только специальный формат (например, вам нужно добавить обнаружение знаков, если разрешены отрицательные значения и т. Д., Или вы можете просто использовать atof, если нужны все возможные форматы).

Если вы захотите проанализировать файл быстрее, вам, вероятно, потребуется использовать многопоточность — например, один поток анализирует только строковые значения, а другой — один или несколько потоков, преобразующих загруженные строковые значения в числа с плавающей запятой. В этом случае вам, вероятно, даже не понадобится файл с отображенной памятью, так как обычного чтения из буферизованного файла может быть достаточно (файл все равно будет прочитан только один раз).

1

Несколько быстрых комментариев к вашему коду:
1) вы не резервируете место для своего вектора, поэтому он выполняет расширение каждый раз, когда вы добавляете значение. Вы прочитали количество точек из файла, поэтому вызывайте функцию резерва (N) после очистки ().

2) вы заставляете карту всего файла одним ударом, который будет работать на 64 битах, но, вероятно, будет медленным, и вынуждает другое выделение того же объема памяти с помощью strBuffer << пиксельный буфер;

http://www.boost.org/doc/libs/1_53_0/doc/html/interprocess/sharedmemorybetweenprocesses.html#interprocess.sharedmemorybetweenprocesses.mapped_file.mapped_file_mapping_regions показывает, как получитьRegion

Используйте цикл через getRegion, чтобы загрузить оценочный кусок данных, содержащий много строк. Вам придется обрабатывать частичные буферы — каждый getRegion, скорее всего, будет заканчиваться частью строки, которую вам нужно сохранить, и присоединиться к следующему частичному буферу, начиная с следующей области.

0
По вопросам рекламы [email protected]