It might be worth considering using std::basic_streambuf
as apposed to std::basic_ostream
. You'd need to do the double
formatting yourself, but this may still be quicker. Here's an example which shows how to do this for a single std::vector<double>
, it should be fairly easy to generalise to a 2D version.
// streambuf.cpp
#include <fstream>
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <cstddef>
#include <random>
#include <chrono>
void write_basic_ostream(const std::vector<double>& v, std::ofstream& of)
{
std::copy(v.cbegin(), v.cend(), std::ostream_iterator<double> {of, " "});
}
void write_basic_streambuf(const std::vector<double>& v, std::ofstream& of)
{
std::string str {};
str.reserve(v.size() * (1 + sizeof(double)));
for (const auto& e : v) {
str += std::to_string(e);
str += " ";
}
std::copy(str.cbegin(), str.cend() - 1, std::ostreambuf_iterator<char>(of));
}
int main()
{
std::size_t n {10000000};
std::default_random_engine generator {};
std::uniform_real_distribution<double> distribution {0.0, 1.0};
std::vector<double> v(n);
std::generate_n(v.begin(), n, [&generator, &distribution] { return distribution(generator); });
std::ofstream of1 {"/Users/you/test_basic_ostream.txt"};
std::ofstream of2 {"/Users/you/test_basic_streambuf.txt"};
auto start = std::chrono::system_clock::now();
write_basic_ostream(v, of1);
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
std::cout << "basic_ostream: " << duration.count() << std::endl;
start = std::chrono::system_clock::now();
write_basic_streambuf(v, of2);
end = std::chrono::system_clock::now();
duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
std::cout << "write_basic_streambuf: " << duration.count() << std::endl;
return 0;
}
Some quick benchmarks has write_basic_ostream
about 20% slower than write_basic_streambuf
on my system (g++ streambuf.cpp -std=c++14 -O3 -o test
).