Производительность создания С++ std::string с входного итератора

Я делаю что-то очень простое: разбивая весь текстовый файл с диска на std::string. Мой текущий код в основном делает это:

std::ifstream f(filename);
return std::string(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());

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

Существует ли риск того, что построение строки потребует много перераспределений? Было бы лучше (то есть быстрее) использовать seekg()/tellg() для вычисления размера файла и reserve() столько места в строке перед выполнением чтения?

Ответ 1

Я сравнил вашу реализацию (1), mine (2) и два других (3 и 4), которые я нашел в stackoverflow.

Результаты (в среднем 100 прогонов, по времени с использованием gettimeofday, файл был 40 абзацев lorem ipsum):

  • readFile1: 764
  • readFile2: 104
  • readFile3: 129
  • readFile4: 402

Реализации:

string readFile1(const string &fileName)
{
    ifstream f(fileName.c_str());
    return string(std::istreambuf_iterator<char>(f),
            std::istreambuf_iterator<char>());
}

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(&bytes[0], fileSize);

    return string(&bytes[0], fileSize);
}

string readFile3(const string &fileName)
{
    string data;
    ifstream in(fileName.c_str());
    getline(in, data, string::traits_type::to_char_type(
                      string::traits_type::eof()));
    return data;
}

string readFile4(const std::string& filename)
{
    ifstream file(filename.c_str(), ios::in | ios::binary | ios::ate);

    string data;
    data.reserve(file.tellg());
    file.seekg(0, ios::beg);
    data.append(istreambuf_iterator<char>(file.rdbuf()),
                istreambuf_iterator<char>());
    return data;
}

Ответ 2

Что происходит с производительностью, если вы попытаетесь это сделать? Вместо того, чтобы спрашивать: "Какой путь быстрее?" вы можете думать "эй, я могу измерить это".

Настройте цикл, который читает файл определенного размера 10000 раз или что-то еще, и время его. Затем сделайте это с помощью метода reserve() и времени. Попробуйте его с несколькими различными размерами файлов (от маленького до огромного) и посмотрите, что вы получаете.

Ответ 3

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

Если это правильно, он будет работать с чем-то похожим на удвоение объема внутреннего хранилища при каждом его отсутствии. В этом случае для n символов в файле будут записаны выделения журнала [n, 2] и удаления памяти, а также n * Log [n, 2] отдельные копии символов вместо простого копирования символов в строку.

Как заметил Грег, вы можете также проверить его. По его словам, попробуйте его для разных размеров файлов для обоих методов. Кроме того, вы можете использовать следующее, чтобы получить некоторые количественные тайминги.

#include<time.h>
#include<iostream>

...

clock_t time1=0, time2=0, delta;
float seconds;

time1=clock();

//Put code to be timed here

time2=clock();

delta= time2-time1;

seconds =(((float)delta)/((float)CLOCKS_PER_SEC));

std::cout<<"The operation took: "<<seconds<<" seconds."<<std::endl;

...

это должно сделать трюк для времени.