views:

308

answers:

4

I am writing an embedded app. In some places, I use std::ostringstream a lot, since it is very convenient for my purposes. However, I just discovered that the performance hit is extreme since adding data to the stream results in a lot of calls to malloc and free. Is there any way to avoid it?

My first thought was making the ostringstream static and resetting it using ostringstream::set(""). However, this can't be done as I need the functions to be reentrant.

+2  A: 

If you know how big the data is before creating the stream you could use ostrstream whose constructor can take a buffer as a parameter. Thus there will be no memory management of the data.

Mark
+2  A: 

Probably the approved way of dealing with this would be to create your own basic_stringbuf object to use with your ostringstream. For that, you have a couple of choices. One would be to use a fixed-size buffer, and have overflow simply fail when/if you try to create output that's too long. Another possibility would be to use a vector as the buffer. Unlike std::string, vector guarantees that appending data will have amortized constant complexity. It also never releases data from the buffer unless you force it to, so it'll normally grow to the maximum size you're dealing with. From that point, it shouldn't allocate or free memory unless you create a string that's beyond the length it currently has available.

Jerry Coffin
I checked the source of the STL implementation I am using, if I pass it a string to the ostringstream constructor it just makes a copy of that string...
kotlinski
Yes, I would expect that. I'd advising creating your own stringbuf object, and attaching a stream to it.
Jerry Coffin
If you want to pass a buffer in use ostrstream not ostringstream
Mark
@Mark:Maybe. If you really want a fixed-size buffer, that's a halfway reasonable possibility. It's not even close to a substitute for one that uses `vector<char>` as its buffer though.
Jerry Coffin
Tru but answer gave a fixed size buffer
Mark
@mark: as one possibility, but it also gave the vector possibility. Truthfully, even if you want a fixed buffer, I'm not sure strstreams are necessarily the best answer. They're included exclusively for backward compatibility, but deprecated from day 1.
Jerry Coffin
+2  A: 

Well, Booger's solution would be to switch to sprintf(). It's unsafe, and error-prone, but it is often faster.

Not always though. We can't use it (or ostringstream) on my real-time job after initialization because both perform memory allocations and deallocations.

Our way around the problem is to jump through a lot of hoops to make sure that we perform all string conversions at startup (when we don't have to be real-time yet). I do think there was one situation where we wrote our own converter into a fixed-sized stack-allocated array. We have some constraints on size we can count on for the specific conversions in question.

For a more general solution, you may consider writing your own version of ostringstream that uses a fixed-sized buffer (with error-checking on the bounds being stayed within, of course). It would be a bit of work, but if you have a lot of those stream operations it might be worth it.

T.E.D.
What do you use instead?
kotlinski
I started to add that, but backed out. I'll go ahead and put it in for you.
T.E.D.
You could at least use snprintf (with a stack buffer), but I still wouldn't advocate this unless profiling shows it provides benefit you need and you decide the drawbacks are worth using it.
Mark B
I haven't checked snprintf, but I suspect it would have the same non-determinsim problem sprintf does on our platform. If you think about it, neither should be doing dynamic memory allocations...but yet our sprintf does. :-(
T.E.D.
Profiling definitely shows malloc/free are crazy expensive on our system, we ran into it before...
kotlinski
A: 

std::ostringsteam is a convenience interface. It links a std::string to a std::ostream by providing a custom std::streambuf. You can implement your own std::streambuf. That allows you to do the entire memory management. You still get the nice formatting of std::ostream, but you have full control over the memory management. Of course, the consequence is that you get your formatted output in a char[] - but that's probably no big problem if you're an embedded developer.

MSalters