Should I preallocate std::stringstream?

后端 未结 4 796
终归单人心
终归单人心 2020-12-16 10:39

I use std::stringstream extensively to construct strings and error messages in my application. The stringstreams are usually very short life automa

相关标签:
4条回答
  • 2020-12-16 11:07

    The Bad

    This is an old question, but even as of C++1z/C++2a in Visual Studio 2019, stringstream has no ideal way of reserving a buffer.

    The other answers to this question do not work at all and for the following reasons:

    • calling reserve on an empty string yields an empty string, so stringstream constructor doesn't need to allocate to copy the contents of that string.

    • seekp on a stringstream still seems to be undefined behavior and/or does nothing.

    The Good

    This code segment works as expected, with ss being preallocated with the requested size.

    std::string dummy(reserve, '\0');
    std::stringstream ss(dummy);
    dummy.clear();
    dummy.shrink_to_fit();
    

    The code can also be written as a one-liner std::stringstream ss(std::string(reserve, '\0'));.

    The Ugly

    What really happens in this code segment is the following:

    • dummy is preallocated with the reserve, and the buffer is subsequently filled with null bytes (required for the constructor).
    • stringstream is constructed with dummy. This copies the entire string's contents into an internal buffer, which is preallocated.
    • dummy is then cleared and then erased, freeing up its allocation.

    This means that in order to preallocate a stringstream, two allocations, one fill, and one copy takes place. The worst part is that during the expression, twice as much memory is needed for the desired allocation. Yikes!

    For most use cases, this might not matter at all and it's OK to take the extra fill and copy hit to have fewer reallocations.

    0 讨论(0)
  • 2020-12-16 11:16

    I'm not sure, but I suspect that stringbuf of stringstream is tightly related with resulted string. So I suspect that you can use ss.seekp(reserved-1); ss.put('\0'); to reserve reserved amount of bytes inside of underlying string of ss. Actually I'd like to see something like ss.seekp(reserved); ss.trunc();, but there is no trunc() method for streams.

    0 讨论(0)
  • 2020-12-16 11:16

    Although "mucking around with the stringstream's rdbuf...is probably Very Easy To Get Wrong", I went ahead and hacked together a proof-of-concept anyway for fun, as it has always bugged me that there is no easy way to reserve storage for stringstream. Again, as @luke said, you are probably better off optimizing what your profiler tells you needs optimizing, so this is just to address "What if I want to do it anyway?".

    Instead of mucking around with stringstream's rdbuf, I made my own, which does pretty much the same thing. It implements only the minimum, and uses a string as a buffer. Don't ask me why I called it a VECTOR_output_stream. This is just a quickly-hacked-together thing.

    constexpr auto preallocated_size = 256;
    auto stream = vector_output_stream(preallocated_size);
    stream << "My parrot ate " << 3 << " cookies.";
    cout << stream.str() << endl;
    
    0 讨论(0)
  • 2020-12-16 11:27

    Have you profiled your execution, and found them to be a source of slow down?

    Consider their usage. Are they mostly for error messages outside the normal flow of your code?

    As far as reserving space...

    Some implementations probably reserve a small buffer before any allocation takes place for the stringstream. Many implementations of std::string do this.

    Another option might be (untested!)

    std::string str;
    str.reserve(50);
    std::stringstream sstr(str);
    

    You might find some more ideas in this gamedev thread.

    edit:

    Mucking around with the stringstream's rdbuf might also be a solution. This approach is probably Very Easy To Get Wrong though, so please be sure it's absolutely necessary. Definitely not elegant or concise.

    0 讨论(0)
提交回复
热议问题