Ускорение записи больших файлов на диск в C ++ - PullRequest
1 голос
/ 09 октября 2019

Мне нужно записать большой CSV-файл на диск. Я уменьшил проблему до кода ниже. Когда я компилирую с VS 2017, запускается на моем компьютере с Windows 7, он работает в среднем за 26 секунд. Может ли кто-нибудь предложить способ ускорить это без изменения контейнера данных или формата вывода? Любая помощь будет оценена.

PS: Вероятно, очевидно, но ускорение должно быть до базового варианта на вашем оборудовании

Я пытался использовать fopen и fprintf, но получил худшие результаты. Я также безуспешно пытался установить размер буфера.

#include <iostream>
#include <iomanip>
#include <fstream> 
#include <chrono>
#include <vector>
#include <string>

typedef std::chrono::high_resolution_clock Clock;
typedef std::vector<double> VecD;
typedef std::vector<VecD> VecVecD;

void test_file_write_stream() {
    VecVecD v(10000, VecD(2000, 1.23456789));
    const std::string delimiter(",");
    const std::string file_path("c:\\junk\\speedtest.csv");
    auto t1_stream = Clock::now();
    std::ofstream ostream(file_path.c_str());
    if (!ostream.good())
        return;
    ostream << std::setprecision(12);

    for (const auto & row : v) {
        for (const auto & col : row) {
            ostream << col << delimiter;
        }
        ostream << std::endl;
    }
    auto t2_stream = Clock::now();
    std::cout << "Stream test: " << std::chrono::duration_cast<std::chrono::microseconds>(t2_stream - t1_stream).count() / 1.0e6 << " seconds" << std::endl;
}

void main(int argc, char * argv[]) {
    test_file_write_stream();
}

Тест потока: 26,2086 секунд

Ответы [ 2 ]

0 голосов
/ 09 октября 2019

То, что вы не хотите использовать, это файлы с отображением в памяти в соответствии с wikipedi :

Преимущество сопоставления памяти с файлом заключается в увеличении I /O производительность, особенно при использовании больших файлов .

Почему? потому что данные не нужно копировать в течение дополнительного времени - и вы должны увидеть увеличение приблизительного значения на 50% -100% или даже больше.

имеет очень аккуратный интерфейс в . У меня нет тестового стенда для этого атм., Но что-то аля:

boost::interprocess::file_mapping fm(filename, ...);
boost::interprocess::mapped_region region(fm, ...);
//mapped_region  is a memory mapped file

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

https://docs.microsoft.com/en-us/dotnet/standard/io/memory-mapped-files

0 голосов
/ 09 октября 2019

Я попробовал ваш код и сделал несколько небольших оптимизаций хипшота, но получил непротиворечивые результаты. Базовая линия была для меня ~ 14 с, и она все время оставалась близкой к этому. Хорошая работа -O3.

Я заменил ваш std::vector<std::vector<double>> одномерной версией и std::copy записал его на диск с незначительным улучшением.

Только когда я сдался ивыгрузил память как есть (, а не как .csv) на диск, у меня было до 1-3 секунд.

Большой недостаток в том, что то, что я сбросил, не переносимов любом случае. Можно надеяться, что в удачный день его можно будет снова прочитать на том же компьютере, но я бы не рекомендовал выводить double s таким образом:

#include <chrono>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

typedef std::chrono::high_resolution_clock Clock;

// custom 2D array with a 1D memory layout
template<typename T>
class array2d {
public:
    array2d(size_t h, size_t w, const T& value = T{}) : data_(h * w, value), w_(w) {}

    inline double* operator[](size_t y) { return &data_[y * w_]; }
    inline double const* operator[](size_t y) const { return &data_[y * w_]; }

    inline size_t width() const { return w_; }

    T const* data() const { return data_.data(); }
    size_t size() const { return data_.size(); }

private:
    std::vector<T> data_;
    size_t w_;
};

using VecVecD = array2d<double>;

void test_file_write_stream() {
    VecVecD v(10000, 2000, 1.23456789);

    const std::string delimiter(",");
    const std::string file_path("c:\\junk\\speedtest.csv");

    auto t1_stream = Clock::now();

    std::ofstream ostream(file_path.c_str(), std::ios::binary);
    if(!ostream) return;

    ostream << std::setprecision(12);

    /* this may give a somewhat better performance than yours, but not much:

    std::copy(v.data(), v.data() + v.size(),
              std::ostream_iterator<double>(ostream, delimiter));
    */

    // non-portable binary dump
    ostream.write(reinterpret_cast<const char*>(v.data()),
                  static_cast<std::streamsize>(v.size() * sizeof(double)));

    auto t2_stream = Clock::now();

    auto elapsed_s =
        std::chrono::duration_cast<std::chrono::seconds>(t2_stream - t1_stream);
    std::cout << "Stream test: " << elapsed_s.count() << " seconds" << std::endl;
}

int main(int, char**) {
    test_file_write_stream();
}
...