C ++ Быстрый способ загрузить большой текстовый файл в векторе <string> - PullRequest
0 голосов
/ 08 октября 2018

У меня есть файл ~ 12.000.000 шестнадцатеричных строк и 1,6 ГБ. Пример файла:

999CBA166262923D53D3EFA72F5C4E8EE1E1FF1E7E33C42D0CE8B73604034580F2
889CBA166262923D53D3EFA72F5C4E8EE1E1FF1E7E33C42D0CE8B73604034580F2

Пример кода:

vector<string>  buffer;

ifstream fe1("strings.txt");
string line1;
    while (getline(fe1, line1)) {
        buffer.push_back(line1);
    }

Теперь загрузка занимает около 20 минут,Любые предложения, как ускорить?Заранее большое спасибо.

Ответы [ 3 ]

0 голосов
/ 08 октября 2018

Вы можете прочитать весь файл в память.Это может быть сделано с потоками C ++, или вы можете получить еще большую производительность, используя API-интерфейсы для конкретной платформы, такие как файлы с отображением в памяти или их собственные API для чтения файлов.

Как только вы получите этот блок данных,для исполнения вы хотите избежать дальнейших копий и использовать его на месте.В C ++ 17 у вас есть std::string_view, который похож на std::string, но использует существующие строковые данные, избегая копирования.В противном случае вы можете просто работать со строками в стиле C char*, либо заменив символ новой строки на ноль (\0), используя пару указателей (начало / конец) или указатель и размер.

ЗдесьЯ использовал string_view, я также предполагал, что переводы строки всегда \n и что в конце есть перевод строки.Возможно, вам придется настроить цикл, если это не так.Предполагая, что размер vector также увеличит производительность, вы можете сделать это по длине файла.Я также пропустил некоторую обработку ошибок.

std::fstream is("data.txt", std::ios::in | std::ios::binary);
is.seekg(0, std::ios::end);
size_t data_size = is.tellg();
is.seekg(0, std::ios::beg);
std::unique_ptr<char[]> data(new char[data_size]);
is.read(data.get(), data_size);


std::vector<std::string_view> strings;
strings.reserve(data_size / 40); // If you have some idea, avoid re-allocations as general practice with vector etc.
for (size_t i = 0, start = 0; i < data_size; ++i)
{
    if (data[i] == '\n') // End of line, got string
    {
        strings.emplace_back(data.get() + start, i - start);
        start = i + 1;
    }
}

Чтобы получить немного больше производительности, вы можете запустить цикл для выполнения работы ЦП параллельно с файловым вводом-выводом.Это может быть сделано с потоками или с помощью IO асинхронного файла для конкретной платформы.Однако в этом случае цикл будет очень быстрым, поэтому выигрыш будет невелик.

0 голосов
/ 21 июля 2019

Вы можете просто выделить достаточно оперативной памяти и прочитать весь текстовый файл почти сразу.Чем вы можете получить доступ к данным в оперативной памяти по указателю памяти.Я прочитал весь 4 ГБ текстовый файл примерно за 3 секунды.

0 голосов
/ 08 октября 2018

Загрузка большого текстового файла в std::vector<std::string> довольно неэффективна и расточительна, поскольку она выделяет кучу памяти для каждого std::string и перераспределяет вектор несколько раз.Каждое из этих распределений кучи требует скрытой информации о ведении кучи (обычно 8 байт на распределение в 64-битной системе) , а для каждой строки требуется объект std::string (8-32 байта в зависимости отстандартная библиотека), поэтому загружаемый таким образом файл занимает гораздо больше места в оперативной памяти, чем на диске.

Один быстрый способ - отобразить файл в память и внедрить итераторы для обхода строк в нем.Это позволяет избежать проблем, упомянутых выше.

Рабочий пример:

#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range_core.hpp>

#include <iostream>

class LineIterator
    : public boost::iterator_facade<
          LineIterator,
          boost::iterator_range<char const*>,
          boost::iterators::forward_traversal_tag,
          boost::iterator_range<char const*>
          >
{
    char const *p_, *q_;
    boost::iterator_range<char const*> dereference() const { return {p_, this->next()}; }
    bool equal(LineIterator b) const { return p_ == b.p_; }
    void increment() { p_ = this->next(); }
    char const* next() const { auto p = std::find(p_, q_, '\n'); return p + (p != q_); }
    friend class boost::iterator_core_access;

public:
    LineIterator(char const* begin, char const* end) : p_(begin), q_(end) {}
};

inline boost::iterator_range<LineIterator> crange(boost::interprocess::mapped_region const& r) {
    auto p = static_cast<char const*>(r.get_address());
    auto q = p + r.get_size();
    return {LineIterator{p, q}, LineIterator{q, q}};
}

inline std::ostream& operator<<(std::ostream& s, boost::iterator_range<char const*> const& line) {
    return s.write(line.begin(), line.size());
}

int main() {
    boost::interprocess::file_mapping file("/usr/include/gnu-versions.h", boost::interprocess::read_only);
    boost::interprocess::mapped_region memory(file, boost::interprocess::read_only);

    unsigned n = 0;
    for(auto line : crange(memory))
        std::cout << n++ << ' ' << line;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...