2016-09-28 7 views
51

В настоящее время я пишу набор двойников из вектора в текстовый файл, например так:Быстрый способ записи данных из станд :: вектор в текстовый файл

std::ofstream fout; 
fout.open("vector.txt"); 

for (l = 0; l < vector.size(); l++) 
    fout << std::setprecision(10) << vector.at(l) << std::endl; 

fout.close(); 

Но это занимает много времени заканчивать. Есть ли более быстрый или более эффективный способ сделать это? Мне бы хотелось увидеть и изучить его.

+3

Как большой ваш вектор? и как долго «какое-то время»? –

+1

Что содержит ваш вектор? –

+1

его, как 300k удваивает, и занимает почти 3 минуты. –

ответ

31

Ваш алгоритм имеет два детали:

  1. Сериализация двойных чисел в строку или буфер символов.

  2. Написать результаты в файл.

Первый элемент может быть улучшена (> 20%) с помощью Sprintf или fmt. Второй элемент может ускоряться путем кэширования результатов в буфер или расширения размера буфера потока выходных файлов, прежде чем записывать результаты в выходной файл. Вы не должны использовать std :: endl, потому что it is much slower than using "\n". Если вы все еще хотите сделать это быстрее, тогда напишите свои данные в двоичном формате. Ниже приведен мой полный образец кода, который включает в себя мои предлагаемые решения и один от Эдгара Рокьяна. Я также включил предложения Ben Voigt и Matthieu M в тестовый код.

#include <algorithm> 
#include <cstdlib> 
#include <fstream> 
#include <iomanip> 
#include <iostream> 
#include <iterator> 
#include <vector> 

// https://github.com/fmtlib/fmt 
#include "fmt/format.h" 

// http://uscilab.github.io/cereal/ 
#include "cereal/archives/binary.hpp" 
#include "cereal/archives/json.hpp" 
#include "cereal/archives/portable_binary.hpp" 
#include "cereal/archives/xml.hpp" 
#include "cereal/types/string.hpp" 
#include "cereal/types/vector.hpp" 

// https://github.com/DigitalInBlue/Celero 
#include "celero/Celero.h" 

template <typename T> const char* getFormattedString(); 
template<> const char* getFormattedString<double>(){return "%g\n";} 
template<> const char* getFormattedString<float>(){return "%g\n";} 
template<> const char* getFormattedString<int>(){return "%d\n";} 
template<> const char* getFormattedString<size_t>(){return "%lu\n";} 


namespace { 
    constexpr size_t LEN = 32; 

    template <typename T> std::vector<T> create_test_data(const size_t N) { 
     std::vector<T> data(N); 
     for (size_t idx = 0; idx < N; ++idx) { 
      data[idx] = idx; 
     } 
     return data; 
    } 

    template <typename Iterator> auto toVectorOfChar(Iterator begin, Iterator end) { 
     char aLine[LEN]; 
     std::vector<char> buffer; 
     buffer.reserve(std::distance(begin, end) * LEN); 
     const char* fmtStr = getFormattedString<typename std::iterator_traits<Iterator>::value_type>(); 
     std::for_each(begin, end, [&buffer, &aLine, &fmtStr](const auto value) { 
      sprintf(aLine, fmtStr, value); 
      for (size_t idx = 0; aLine[idx] != 0; ++idx) { 
       buffer.push_back(aLine[idx]); 
      } 
     }); 
     return buffer; 
    } 

    template <typename Iterator> 
    auto toStringStream(Iterator begin, Iterator end, std::stringstream &buffer) { 
     char aLine[LEN]; 
     const char* fmtStr = getFormattedString<typename std::iterator_traits<Iterator>::value_type>(); 
     std::for_each(begin, end, [&buffer, &aLine, &fmtStr](const auto value) {    
      sprintf(aLine, fmtStr, value); 
      buffer << aLine; 
     }); 
    } 

    template <typename Iterator> auto toMemoryWriter(Iterator begin, Iterator end) { 
     fmt::MemoryWriter writer; 
     std::for_each(begin, end, [&writer](const auto value) { writer << value << "\n"; }); 
     return writer; 
    } 

    // A modified version of the original approach. 
    template <typename Container> 
    void original_approach(const Container &data, const std::string &fileName) { 
     std::ofstream fout(fileName); 
     for (size_t l = 0; l < data.size(); l++) { 
      fout << data[l] << std::endl; 
     } 
     fout.close(); 
    } 

    // Replace std::endl by "\n" 
    template <typename Iterator> 
    void improved_original_approach(Iterator begin, Iterator end, const std::string &fileName) { 
     std::ofstream fout(fileName); 
     const size_t len = std::distance(begin, end) * LEN; 
     std::vector<char> buffer(len); 
     fout.rdbuf()->pubsetbuf(buffer.data(), len); 
     for (Iterator it = begin; it != end; ++it) { 
      fout << *it << "\n"; 
     } 
     fout.close(); 
    } 

    // 
    template <typename Iterator> 
    void edgar_rokyan_solution(Iterator begin, Iterator end, const std::string &fileName) { 
     std::ofstream fout(fileName); 
     std::copy(begin, end, std::ostream_iterator<double>(fout, "\n")); 
    } 

    // Cache to a string stream before writing to the output file 
    template <typename Iterator> 
    void stringstream_approach(Iterator begin, Iterator end, const std::string &fileName) { 
     std::stringstream buffer; 
     for (Iterator it = begin; it != end; ++it) { 
      buffer << *it << "\n"; 
     } 

     // Now write to the output file. 
     std::ofstream fout(fileName); 
     fout << buffer.str(); 
     fout.close(); 
    } 

    // Use sprintf 
    template <typename Iterator> 
    void sprintf_approach(Iterator begin, Iterator end, const std::string &fileName) { 
     std::stringstream buffer; 
     toStringStream(begin, end, buffer); 
     std::ofstream fout(fileName); 
     fout << buffer.str(); 
     fout.close(); 
    } 

    // Use fmt::MemoryWriter (https://github.com/fmtlib/fmt) 
    template <typename Iterator> 
    void fmt_approach(Iterator begin, Iterator end, const std::string &fileName) { 
     auto writer = toMemoryWriter(begin, end); 
     std::ofstream fout(fileName); 
     fout << writer.str(); 
     fout.close(); 
    } 

    // Use std::vector<char> 
    template <typename Iterator> 
    void vector_of_char_approach(Iterator begin, Iterator end, const std::string &fileName) { 
     std::vector<char> buffer = toVectorOfChar(begin, end); 
     std::ofstream fout(fileName); 
     fout << buffer.data(); 
     fout.close(); 
    } 

    // Use cereal (http://uscilab.github.io/cereal/). 
    template <typename Container, typename OArchive = cereal::BinaryOutputArchive> 
    void use_cereal(Container &&data, const std::string &fileName) { 
     std::stringstream buffer; 
     { 
      OArchive oar(buffer); 
      oar(data); 
     } 

     std::ofstream fout(fileName); 
     fout << buffer.str(); 
     fout.close(); 
    } 
} 

// Performance test input data. 
constexpr int NumberOfSamples = 5; 
constexpr int NumberOfIterations = 2; 
constexpr int N = 3000000; 
const auto double_data = create_test_data<double>(N); 
const auto float_data = create_test_data<float>(N); 
const auto int_data = create_test_data<int>(N); 
const auto size_t_data = create_test_data<size_t>(N); 

CELERO_MAIN 

BASELINE(DoubleVector, original_approach, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("origsol.txt"); 
    original_approach(double_data, fileName); 
} 

BENCHMARK(DoubleVector, improved_original_approach, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("improvedsol.txt"); 
    improved_original_approach(double_data.cbegin(), double_data.cend(), fileName); 
} 

BENCHMARK(DoubleVector, edgar_rokyan_solution, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("edgar_rokyan_solution.txt"); 
    edgar_rokyan_solution(double_data.cbegin(), double_data.end(), fileName); 
} 

BENCHMARK(DoubleVector, stringstream_approach, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("stringstream.txt"); 
    stringstream_approach(double_data.cbegin(), double_data.cend(), fileName); 
} 

BENCHMARK(DoubleVector, sprintf_approach, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("sprintf.txt"); 
    sprintf_approach(double_data.cbegin(), double_data.cend(), fileName); 
} 

BENCHMARK(DoubleVector, fmt_approach, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("fmt.txt"); 
    fmt_approach(double_data.cbegin(), double_data.cend(), fileName); 
} 

BENCHMARK(DoubleVector, vector_of_char_approach, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("vector_of_char.txt"); 
    vector_of_char_approach(double_data.cbegin(), double_data.cend(), fileName); 
} 

BENCHMARK(DoubleVector, use_cereal, NumberOfSamples, NumberOfIterations) { 
    const std::string fileName("cereal.bin"); 
    use_cereal(double_data, fileName); 
} 

// Benchmark double vector 
BASELINE(DoubleVectorConversion, toStringStream, NumberOfSamples, NumberOfIterations) { 
    std::stringstream output; 
    toStringStream(double_data.cbegin(), double_data.cend(), output); 
} 

BENCHMARK(DoubleVectorConversion, toMemoryWriter, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toMemoryWriter(double_data.cbegin(), double_data.cend())); 
} 

BENCHMARK(DoubleVectorConversion, toVectorOfChar, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toVectorOfChar(double_data.cbegin(), double_data.cend())); 
} 

// Benchmark float vector 
BASELINE(FloatVectorConversion, toStringStream, NumberOfSamples, NumberOfIterations) { 
    std::stringstream output; 
    toStringStream(float_data.cbegin(), float_data.cend(), output); 
} 

BENCHMARK(FloatVectorConversion, toMemoryWriter, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toMemoryWriter(float_data.cbegin(), float_data.cend())); 
} 

BENCHMARK(FloatVectorConversion, toVectorOfChar, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toVectorOfChar(float_data.cbegin(), float_data.cend())); 
} 

// Benchmark int vector 
BASELINE(int_conversion, toStringStream, NumberOfSamples, NumberOfIterations) { 
    std::stringstream output; 
    toStringStream(int_data.cbegin(), int_data.cend(), output); 
} 

BENCHMARK(int_conversion, toMemoryWriter, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toMemoryWriter(int_data.cbegin(), int_data.cend())); 
} 

BENCHMARK(int_conversion, toVectorOfChar, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toVectorOfChar(int_data.cbegin(), int_data.cend())); 
} 

// Benchmark size_t vector 
BASELINE(size_t_conversion, toStringStream, NumberOfSamples, NumberOfIterations) { 
    std::stringstream output; 
    toStringStream(size_t_data.cbegin(), size_t_data.cend(), output); 
} 

BENCHMARK(size_t_conversion, toMemoryWriter, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toMemoryWriter(size_t_data.cbegin(), size_t_data.cend())); 
} 

BENCHMARK(size_t_conversion, toVectorOfChar, NumberOfSamples, NumberOfIterations) { 
    celero::DoNotOptimizeAway(toVectorOfChar(size_t_data.cbegin(), size_t_data.cend())); 
} 

Ниже приведены результаты работы, полученные в моем окне Linux с использованием лязг-3.9.1 и -O3 флаг. Я использую Celero для сбора всех результатов.

Timer resolution: 0.001000 us 
----------------------------------------------------------------------------------------------------------------------------------------------- 
    Group  | Experiment | Prob. Space |  Samples  | Iterations | Baseline  | us/Iteration | Iterations/sec | 
----------------------------------------------------------------------------------------------------------------------------------------------- 
DoubleVector | original_approa | Null   |    10 |    4 |   1.00000 | 3650309.00000 |   0.27 | 
DoubleVector | improved_origin | Null   |    10 |    4 |   0.47828 | 1745855.00000 |   0.57 | 
DoubleVector | edgar_rokyan_so | Null   |    10 |    4 |   0.45804 | 1672005.00000 |   0.60 | 
DoubleVector | stringstream_ap | Null   |    10 |    4 |   0.41514 | 1515377.00000 |   0.66 | 
DoubleVector | sprintf_approac | Null   |    10 |    4 |   0.35436 | 1293521.50000 |   0.77 | 
DoubleVector | fmt_approach | Null   |    10 |    4 |   0.34916 | 1274552.75000 |   0.78 | 
DoubleVector | vector_of_char_ | Null   |    10 |    4 |   0.34366 | 1254462.00000 |   0.80 | 
DoubleVector | use_cereal  | Null   |    10 |    4 |   0.04172 | 152291.25000 |   6.57 | 
Complete. 

Я также тест для числовых алгоритмов преобразования строки для сравнения производительности станда :: stringstream, FMT :: MemoryWriter и зОго :: вектора.

Timer resolution: 0.001000 us 
----------------------------------------------------------------------------------------------------------------------------------------------- 
    Group  | Experiment | Prob. Space |  Samples  | Iterations | Baseline  | us/Iteration | Iterations/sec | 
----------------------------------------------------------------------------------------------------------------------------------------------- 
DoubleVectorCon | toStringStream | Null   |    10 |    4 |   1.00000 | 1272667.00000 |   0.79 | 
FloatVectorConv | toStringStream | Null   |    10 |    4 |   1.00000 | 1272573.75000 |   0.79 | 
int_conversion | toStringStream | Null   |    10 |    4 |   1.00000 | 248709.00000 |   4.02 | 
size_t_conversi | toStringStream | Null   |    10 |    4 |   1.00000 | 252063.00000 |   3.97 | 
DoubleVectorCon | toMemoryWriter | Null   |    10 |    4 |   0.98468 | 1253165.50000 |   0.80 | 
DoubleVectorCon | toVectorOfChar | Null   |    10 |    4 |   0.97146 | 1236340.50000 |   0.81 | 
FloatVectorConv | toMemoryWriter | Null   |    10 |    4 |   0.98419 | 1252454.25000 |   0.80 | 
FloatVectorConv | toVectorOfChar | Null   |    10 |    4 |   0.97369 | 1239093.25000 |   0.81 | 
int_conversion | toMemoryWriter | Null   |    10 |    4 |   0.11741 |  29200.50000 |   34.25 | 
int_conversion | toVectorOfChar | Null   |    10 |    4 |   0.87105 | 216637.00000 |   4.62 | 
size_t_conversi | toMemoryWriter | Null   |    10 |    4 |   0.13746 |  34649.50000 |   28.86 | 
size_t_conversi | toVectorOfChar | Null   |    10 |    4 |   0.85345 | 215123.00000 |   4.65 | 
Complete. 

Из приведенных выше таблиц видно, что:

  1. решение Edgar Rokyan 10% медленнее, чем решение stringstream. Решение, использующее библиотеку fmt, является лучшим для трех изученных типов данных, которые являются double, int и size_t. Решение sprintf + std :: vector на 1% быстрее, чем решение fmt для двойного типа данных. Тем не менее, я не рекомендую решения, которые используют sprintf для производственного кода, потому что они не изящны (все еще написаны в стиле C) и не работают из коробки для разных типов данных, таких как int или size_t.

  2. Результаты тестов также показывают, что fmt является сериализацией типа интегрального интегратора, поскольку он по крайней мере в 7 раз быстрее, чем другие подходы.

  3. Мы можем ускорить этот алгоритм 10x, если мы используем двоичный формат. Этот подход значительно быстрее, чем запись в форматированный текстовый файл, потому что мы делаем только исходную копию из памяти на выходе. Если вы хотите иметь более гибкие и портативные решения, попробуйте cereal или boost::serialization или protocol-buffer. Согласно this performance study, зерновые, кажется, самые быстрые.

+1

В версии 3 вы получите значительно лучшую производительность, если вы используете 'vector ', чтобы скрыть все отдельные строки вместо 'std :: stringstream'. Контрольный вопрос: http://stackoverflow.com/q/4340396/103167 –

+0

Большое спасибо за ссылку. Вы пример кода больше не существует, поэтому я должен угадать, что вы сделали. Я также добавил еще одно решение, использующее fmt (https://github.com/fmtlib/fmt). Из моих результатов тестов std :: vector - это самое быстрое решение, однако это решение не намного быстрее, чем решения, использующие stringstream и fmt :: MemoryWriter. – hungptit

+6

@hungptit: Кэширование к потоку вряд ли будет иметь большое влияние в целом, потому что 'std :: ofstream' * уже * кэширует перед записью в файл. Если, конечно, вы не используете 'std :: endl' после каждого элемента, который вызывает' flush', который * задает * syscall. –

71
std::ofstream fout("vector.txt"); 
fout << std::setprecision(10); 

for(auto const& x : vector) 
    fout << x << '\n'; 

Все, что изменилось было теоретически хуже, производительность в вашей версии кода, но std::endl was the real killer. std::vector::at (с проверкой границ, которая вам не нужна) была бы второй, а затем фактом, что вы не использовали итераторы.

Почему default-construct a std::ofstream, а затем звоните open, когда вы можете сделать это за один шаг? Зачем звонить close, когда RAII (деструктор) заботится о нем для вас? Вы также можете позвонить по телефону

fout << std::setprecision(10) 

только один раз, перед петлей.

Как указано в комментарии ниже, если ваш вектор имеет элементы фундаментального типа, вы можете получить лучшую производительность с помощью for(auto x : vector). Измерьте время работы/проверьте выход сборки.


Просто указать на другую, что бросилось в глаза, это:

for(l = 0; l < vector.size(); l++) 

Что это l? Зачем объявлять его за пределами цикла? Кажется, вам это не нужно во внешнем масштабе, так что нет. А также post-increment.

Результат:

for(size_t l = 0; l < vector.size(); ++l) 

Я прошу прощения за просмотр кода из этого поста.

+4

Кроме того, учитывая, что это вектор удвоений, использование переменной без повторения ('for (auto x: vector)') может быть более быстрым - оно обрабатывает разыменование для двойной копии. –

+0

Благодарю вас, много, как я сказал вам, я просто учился, и я сам учу себя C++ soi, как это, я понимаю, распространены. –

+0

один вопрос, даже если я открою второй поток для записи в другой текстовый файл, мне не нужно закрывать? –

5

У вас есть два основных узких места в вашей программе: вывод и форматирование текста.

Чтобы увеличить производительность, вам необходимо увеличить объем вывода данных за звонок. Например, 1 передача вывода 500 символов быстрее 500 передач 1 символа.

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

Вот пример:

char buffer[1024 * 1024]; 
unsigned int buffer_index = 0; 
const unsigned int size = my_vector.size(); 
for (unsigned int i = 0; i < size; ++i) 
{ 
    signed int characters_formatted = snprintf(&buffer[buffer_index], 
              (1024 * 1024) - buffer_index, 
              "%.10f", my_vector[i]); 
    if (characters_formatted > 0) 
    { 
     buffer_index += (unsigned int) characters_formatted; 
    } 
} 
cout.write(&buffer[0], buffer_index); 

Вы должны сначала попытаться изменить параметры оптимизации в вашем компиляторе, прежде чем возиться с кодом.

+3

io поток буферов под капотом, поэтому это количество усилий не должно быть необходимым. К сожалению, код OP использовался, например, 'std :: endl', который излишним сбросом буфера, победив большую часть этой выгоды. –

+0

Да, буферы потока ввода-вывода находятся под капотом. Тем не менее, этот метод позволяет настраивать буферы, которые могут быть больше, чем буферы io-потока. –

+0

Это. При определенных условиях потоки std могут быть такими же быстрыми, как функции printf C. Однако при неправильных условиях они на порядок медленнее, поэтому, если производительность желательна, используйте функции printf C. – Peter

20

Вы также можете использовать довольно аккуратную форму вывода содержимого любого vector в файл с помощью итераторов и copy.

std::ofstream fout("vector.txt"); 
fout.precision(10); 

std::copy(numbers.begin(), numbers.end(), 
    std::ostream_iterator<double>(fout, "\n")); 

Это решение практически совпадает с решением LogicStuff с точки зрения времени выполнения.Но это также иллюстрирует, как печатать содержимое только с помощью одной функции copy, которая, как я полагаю, выглядит довольно хорошо.

+4

Вместо этого вы можете использовать ostreambuf_iterator. Это избавляет от создания парного сторожевого объекта для каждого предмета. –

+0

Я не знаю о производительности, но этот ответ является самым элегантным, который я видел. И это научило меня о 'std :: ostream_iterator'. +1 – Vality

+1

Наконец-то кто-то выразил намерение в коде :). Для простоты: используйте бесплатные функции для начинающих и конечных итераторов: 'copy (begin (numbers), end (numbers), ostream_iterator ...)'. – xtofl

2

Вот несколько другое решение: сохраните двойники в двоичной форме.

int fd = ::open("/path/to/the/file", O_WRONLY /* whatever permission */); 
::write(fd, &vector[0], vector.size() * sizeof(vector[0])); 

Поскольку вы упомянули, что у вас есть 300 тыс двойников, что составляет 300k * 8 байт = 2.4м, вы можете сохранить их все на локальный файл на диске в менее 0,1 секунды. Единственный недостаток этого метода - сохраненный файл не так читается, как строковое представление, но HexEditor может решить эту проблему.

Если вы предпочитаете более надежный способ, в сети доступно множество библиотек/инструментов для сериализации. Они обеспечивают больше преимуществ, таких как язык-нейтральный, машинно-независимый, гибкий алгоритм сжатия и т.д. Те два я обычно использую:

+0

Это можно сделать с помощью API C++, если вы не хотите ограничивать свое решение Posix. –

+0

@AdrianMcCarthy: C++ предоставляет только API для ввода-вывода символов, 'std :: ios :: binary' только отключает преобразование новой строки, это не отключает шаг кодирования символов. Возможно, шаг кодирования символов - это не-op для некоторых популярных библиотек C++ ... но нет никакого портативного способа убедиться в этом. –

+1

@Ben Voigt: Но любой шаг кодирования символов, по-видимому, был бы обратимым, поэтому нужно просто написать байты содержимого вектора в двоичный поток и прочитать их обратно. (Где в стандарте читается о таком шаге кодирования символов? Я не могу найти ничего, что подсказывает, что «basic_ostream :: write» в бинарный поток будет делать любую кодировку.) –

11

ОК, мне грустно, что есть три решения, которые пытаются дать вам рыбу, но нет решения, которое пытается научить вас, как ловить рыбу.

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

Преобразование двухстрочного кода для 300 000 двухместных номеров не займет 3 минуты на любом компьютере, который отправлен за последние 10 лет.

Запись 3 МБ данных на диск (средний размер 300 000 удваивается) не займет 3 минуты на любом компьютере, который отправлен за последние 10 лет.

Если вы прокомментируете это, я предполагаю, что вы обнаружите, что fout получает покраснение 300 000 раз, и эта промывка идет медленно, потому что это может быть блокирование или полублокирование ввода-вывода. Таким образом, вам необходимо избегать блокировки ввода-вывода. Типичный способ сделать это - подготовить все ваши операции ввода-вывода к одному буферу (создать строковый поток, записать на него), а затем записать этот буфер в физический файл за один раз. Это решение hungptit описывает, за исключением того, что я думаю, что то, что отсутствует, объясняет, ПОЧЕМУ это решение является хорошим решением.

Или, говоря иначе: то, что профайлер скажет вам, заключается в том, что вызов write() (в Linux) или WriteFile() (в Windows) намного медленнее, чем просто копирование нескольких байтов в буфер памяти, потому что это переход на уровне пользователя/ядра. Если std :: endl вызывает это для каждого двойника, у вас будет плохое (медленное) время. Замените его чем-то, что просто остается в пространстве пользователя и помещает данные в ОЗУ!

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

+1

Не проще ли просто настроить буферизацию (полностью буферный, разумный размер буфера, может быть, мегабайт для ПК)? Не очень знакомы std :: streams, может быть, они этого не поддерживают. – hyde

+0

@hyde: буферизация есть. 'Cout << endl' фактически сбрасывает буферы. – xtofl

+0

@xtofl Это строка буферизована, и это то, что убивает производительность, потому что она сбрасывает каждые несколько десятков байтов или что-то еще.Полностью буферизованный будет означать, что он сбрасывается, когда буфер заполнен, поэтому с помощью всего лишь 1 КБ буфера он будет уменьшаться в 10 раз реже и с 1 МБ буфером в 10000 раз реже. – hyde