C++ write to text file fast

2.2k Views Asked by At

I have a large amount of big matrices with values (integers) ranging from -1 to 15, which I want to write to a text file with the function below. The writing speed seems to be about 0.1 MB/s, so I have played around a bit to see if I can make it faster without any results. How do I make it faster?

bool mymap::write_one_mat(string txtfile, matrix& mat)
{
ofstream myfile (txtfile, ios::app|ios::binary);

int element;

if (myfile.is_open())
{
    int rows = mat.get_rows();
    int cols = mat.get_cols();
    myfile << "<";
    for(int i = 1; i <= rows; ++i)
    {
        for(int j = 1; j <= cols; ++j)
        {
            element = mat.get_element(i,j);
            if(element < 0 || element > 9)
            {
                myfile << to_string(element);
            }
            else
            {
                myfile << " ";
                myfile << to_string(element);
            }
        }
    }

    myfile << ">\n";

    myfile.close();
    return true;
}
else
    return false;
}
2

There are 2 best solutions below

1
On

Taking from the comments you have:

bool mymap::write_one_mat(std::string const& txtfile, matrix const& mat)
{
    std::ios_base::sync_with_stdio(false);
    std::ofstream myfile(txtfile, std::ios_base::app | std::ios_base::binary);

    if (myfile.is_open())
    {
        int rows = mat.get_rows();
        int cols = mat.get_cols();

        myfile << "<";
        for (int i = 1; i <= rows; ++i)
        {
            for (int j = 1; j <= cols; ++j)
            {
                int element = mat.get_element(i, j);
                if (!(element < 0 || element > 9))
                    myfile << " ";
                myfile << element;
            }
        }
        myfile << ">\n";
    }
    return static_cast<bool>(myfile);
}

With the addition that txtfile's and mat's type has been changed to a reference to const. This makes sense since your write_one_mat method doesn't modify its parameters. Make sure that mat::get_rows(), mat::get_cols() and get_element() are const methods so they can be called on mat.

1
On

As was commented already, you may want to start removing the unnecessary use of std::to_string(): the stream can happily format integers directly. However, even when formatting integers directly, there is some unnecessary overhead finding about about facets which seems to use a dynamic_cast<...>(..) in most implementations. As a result it may be faster to format the integers manually using something like this:

std::locale loc(std::locale(), new std::num_put<char, char*>());
std::num_put<char, char*> const& np(std::use_fast<std::num_put<char, char*>>(loc));
char buffer[1024];
char* next(buffer);
for (int i(1); i <= rows; ++i) {
     for (int j(1); j <= cols; ++j) {
         int element(mat.get_element(i, j));
         if (element < 0 || element < 9) {
             *next++ = ' ';
         }
         next = np.put(next, myfile, ' ', element);
         if (std::numeric_limits<int>::digits10 + 1 <= (buffer + 1014) - next)) {
             myfile.write(buffer, next - buffer);
             next = buffer;
         }
     }
}
myfile.sputn(buffer, next - buffer);

Using std::num_put<...> directly seems to be the fastest approach (see this graph which graphs the times taken for different compilers using different approaches: shorter is better).

It seems your code writes a large sequence of digits with some odd rule for introducing spaces: are you sure you don't want to put a space after each element and maybe a newline after each row?