2013-06-24 4 views
2

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

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. запаса пространства в станд :: вектор для N * num_points (N = 3, предполагая только X, Y, Z, 6 с нормалей, 9 с нормалей и rgb)
  3. загрузить оставшуюся часть файла в строку
  4. boost :: spirit :: qi :: phrase_parse в вектор.

// код здесь может разобрать файл с 40М точек (> 1 Гб) примерно 14s на моем 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; 
} 
+0

Спасибо graphicsMan !!! Я попробую. – Atul

1

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 отображает весь файл, поэтому нет необходимости передавать размер явно.

+0

Я новичок, чтобы повысить. Можете ли вы объяснить с помощью простого примера, пожалуйста? – Atul

+0

Обновлено, хотя я хотел бы отметить, что я ничего не тестировал. – Hasturkun

0

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

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

http://www.boost.org/doc/libs/1_53_0/doc/html/interprocess/sharedmemorybetweenprocesses.html#interprocess.sharedmemorybetweenprocesses.mapped_file.mapped_file_mapping_regions показывает, как getRegion

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

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; 
} 

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

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

+0

Хороший вопрос о том, что, возможно, не требуется отображение IO с памятью. Я пропустил, что это было просто серийное чтение. Люди забывают, что операционные системы имеют большую оптимизацию, чтобы эффективно работать. Учитывая объем работы, которую я делал за эти годы с быстрым анализом без загрузки всего файла, я немного смущен. Говорите о том, чтобы отвлечься от запроса пользователя на Y, вместо того, чтобы смотреть на задачу X, которую они должны выполнить! –