27

I read somewhere that snprintf is faster than ostringstream. Has anyone has any experiences with it? If yes why is it faster.

kal
  • 28,545
  • 49
  • 129
  • 149

9 Answers9

29

std::ostringstream is not required to be slower, but it is generally slower when implemented. FastFormat's website has some benchmarks.

The Standard library design for streams supports much more than snprintf does. The design is meant to be extensible, and includes protected virtual methods that are called by the publicly exposed methods. This allows you to derive from one of the stream classes, with the assurance that if you overload the protected method you will get the behavior you want. I believe that a compiler could avoid the overhead of the virtual function call, but I'm not aware of any compilers that do.

Additionally, stream operations often use growable buffers internally; which implies relatively slow memory allocations.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
  • Wow - I haven't been to Matthew Wilson's website in a little while - looks like he's had a busy last few months... – Michael Burr Jan 15 '09 at 01:52
  • 1
    The FastFormat tests are biased. Downloading the code, you'll see the snprintf() benchmarks call a function called fastformat_util_snprintf(), which includes some wrapper code that probably throws the tests off. But you're right, the memory management probably is what makes ostringstream slower. – Max Jan 16 '09 at 11:13
  • Thanks for looking at the code. I had only looked at the pretty graphs. – Max Lybbert Jan 20 '09 at 09:10
  • Max, you're not correct about the snprintf() benchmarks. Check the code again – dcw Mar 16 '09 at 08:55
  • I didn't do the benchmarks, and I'm not able to update Matt Wilson's website or his code. I do stand by the original statement (that they aren't designed to be slower, but generally are implemented slower). If you don't like Matt Wilson's benchmarks, I'm sure a little Googling will find others. – Max Lybbert Mar 16 '09 at 22:19
  • Matt Wilson, by the way, wrote the STLSoft libraries, so I'm pretty willing to accept his benchmarks on name alone. – Max Lybbert Mar 16 '09 at 22:22
  • 1
    I think dcw meant the other Max. :) Anyway, FastFormat looks nice, and the std IOStreams library is hopelessly flawed. But the convoluted installation of the library makes it a bit of a nonstarter for me. If I have to mess with env variables, I'll find another library. – jalf Dec 12 '09 at 09:47
  • LOL. Thanks jalf. I had forgotten about this question; and re-reading I think you're right. – Max Lybbert Dec 15 '09 at 07:46
  • 2
    One reason is that string streams usually are thread safe, and snprintf is not, so there is some overhead there. – Johan Kotlinski Aug 12 '10 at 08:21
  • 2
    The `printf` function family supports locales, just like `iostream`. @kotlinski: They are also threadsafe. –  Jul 09 '12 at 19:30
  • Thanks, @JoeWreschnig. I updated the answer after reading the book *Standard C++ Iostreams and Locales: Advanced Programmer's Guide and Reference*, which goes through what IOStreams must do to support locales, but not what `snprintf` and other functions already did. I've removed the reference to locales. – Max Lybbert Jul 10 '12 at 08:16
10

We replaced some stringstreams in inner loops with sprintf (using statically allocated buffers), and this made a big difference, both in msvc and gcc. I imagine that the dynamic memory management of this code:

{
  char buf[100];
  int i = 100;
  sprintf(buf, "%d", i);
  // do something with buf
}

is much simpler than

{
  std::stringstream ss;
  int i = 100;
  ss << i;
  std::string s = ss.str();
  // do something with s
}

but i am very happy with the overall performance of stringstreams.

user52875
  • 3,020
  • 22
  • 21
8

Some guys would possibly tell you about that the functions can't be faster than each other, but their implementation can. That's right i think i would agree.

You are unlikely to ever notice a difference in other than benchmarks. The reason that c++ streams generally tend to be slower is that they are much more flexible. Flexibility most often comes at the cost of either time or code growth.

In this case, C++ streams are based on stream-buffers. In itself, streams are just the hull that keep formatting and error flags in place, and call the right i/o facets of the c++ standard library (for example, num_put to print numbers), that print the values, well formatted, into the underlying stream-buffer connected to the c++ stream.

All this mechanisms - the facets, and the buffers, are implemented by virtual functions. While there is indeed no mark note, those functions must be implemented to be slower than c stdio pendants that fact will make them somewhat slower than using c stdio functions normally (i benchmark'ed that some time ago with gcc/libstdc++ and in fact noticed a slowdown - but which you hardly notice in day-by-day usage).

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
3

Absolutely this is implementation-specific.

But if you really want to know, write two small programs, and compare them. You would need to include typical usage for what you have in mind, the two programs would need to generate the same string, and you would use a profiler to look at the timing information.

Then you would know.

Paul Beckingham
  • 14,495
  • 5
  • 33
  • 67
1

One issue would probably be that the type safety added by ostringstream carries extra overhead. I've not done any measurements, though.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
0

Yes, if you run the function below on a few million numbers with Visual C++ 5.0, the first version takes about twice as long as the second and produces the same output.

Compiling tight loops into a .exe and running the Windows timethis something.exe' or the Linuxtime something' is how I investigate most of my performance curiosities. (`timethis' is available on the web somewhere)

void Hex32Bit(unsigned int n, string &result)
{
#if 0
    stringstream ss;
    ss
        << hex
        << setfill('0')
        << "0x" << setw(8) << n
    ;
    result = ss.str();
#else
    const size_t len = 11;
    char temp[len];
    _snprintf(temp, len, "0x%08x", n);
    temp[len - 1] = '\0';
    result = temp;
#endif
}
  • 2
    Are you compiling with optimization? If not, then your results are not interesting - and if you are, then the compiler is likely to optimize to `void Hexx32Bit(...) {}` – Eric Mar 22 '18 at 21:51
0

It's quite possible that because sprintf is part of the CRT that is written in assembly. The ostringstream is part of the STL, and probably a little more generically written, and has OOP code/overhead to deal with.

George Stocker
  • 57,289
  • 29
  • 176
  • 237
LarryF
  • 4,925
  • 4
  • 32
  • 40
  • 4
    I very strongly doubt anyone has _ever_ implemented `sprintf` in assembly except possibly as an exercise in masochism. All the way back to UNIX V6, the C runtime library has itself been written in C (except for a tiny handful of things that are _impossible_ to write in C, like `setjmp`). – zwol Sep 19 '12 at 15:42
0

As litb said, standard streams support many things we don't always need. Some streams implementation get rid of this never used flexibility, see FAStream for instance.

Community
  • 1
  • 1
Luc Hermitte
  • 31,979
  • 7
  • 69
  • 83
-1

One reason I know that the printf family of functions are faster than the corresponding C++ functions (cout, cin, and other streams) is that the latter do typechecking. As this usually involves some requests to overloaded operators, it can take some time.

In fact, in programming competitions it is often recommended that you use printf et al rather than cout/cin for precisely this reason.

sykora
  • 96,888
  • 11
  • 64
  • 71
  • 4
    This kind of typechecking as well as operator overloads are resolved in compile time. The reduced runtime performance is not explained by them. – Pyry Jahkola Jan 15 '09 at 03:59
  • 4
    In fact, the printf() family has to parse the format at runtime, which does not match the observation – MSalters Jan 15 '09 at 15:23
  • 2
    C++ typechecking isn't a runtime cost. Unless you're talking about *compilation* speed, in which case `` is awful. – Tom Dec 12 '09 at 02:44