为什么snprintf比ostringstream快,或者说它真的更快吗?

27

我在某个地方读到,snprintf比ostringstream更快。有人有过任何相关经验吗?如果有的话,为什么它会更快。

9个回答

29

std::ostringstream不一定比其它方法慢,但通常实现时较慢。 FastFormat网站提供了一些基准测试数据

标准库的流设计支持的功能远远超过snprintf。该设计旨在可扩展,并包括受到公开显示的方法调用的protected virtual 方法。这使您可以从其中一个流类派生,确保如果您重载protected方法,将获得所需的行为。我相信编译器可以避免virtual函数调用的开销,但我不知道有哪些编译器会这样做。

此外,流操作经常在内部使用可增长缓冲区;这意味着相对较慢的内存分配。


1
FastFormat的测试存在偏差。下载代码后,您会看到snprintf()基准测试调用了一个名为fastformat_util_snprintf()的函数,其中包含一些包装器代码,可能会使测试失真。但是,您是正确的,内存管理可能是ostringstream变慢的原因。 - Max
我没有做过基准测试,也不能更新Matt Wilson的网站或他的代码。我仍然坚持原有的说法(它们并不是被设计成更慢,但通常实现地更慢)。如果你不喜欢Matt Wilson的基准测试,我相信通过一些搜索会找到其他的。 - Max Lybbert
1
我认为dcw的意思是另一个Max。:) 无论如何,FastFormat看起来不错,而标准IOStreams库则存在严重缺陷。但安装该库的复杂性使它对我来说有点行不通。如果我必须要弄环境变量,我会找另一个库的。 - jalf
2
一个原因是字符串流通常是线程安全的,而snprintf不是,因此存在一些开销。 - Johan Kotlinski
2
printf 函数族支持本地化,就像 iostream 一样。@kotlinski:它们也是线程安全的。 - user79758
显示剩余6条评论

10

我们在内部循环中使用静态分配的缓冲区以及sprintf(而不是stringstream)来替换一些字符串流,这在msvc和gcc中都产生了很大的差异。我想这段代码的动态内存管理:

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

比这段代码更简单:

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

但我非常满意字符串流的整体性能。


8
一些人可能会告诉你,函数不能比其他函数更快,但它们的实现可以。我认为这是正确的。除了基准测试之外,你不太可能注意到任何区别。C++流通常比较慢的原因是它们更加灵活。灵活性往往是以时间或代码增长的代价为代价的。
在这种情况下,C ++流是基于流缓冲区的。流本身只是保持格式化和错误标志的外壳,并调用与 c++ 标准库相连接的底层流缓冲区的正确 I/O facets(例如 num_put 用于打印数字),将值以良好格式打印到其中。
所有这些机制 - facets 和 buffers 都是通过虚函数实现的。虽然确实没有标记说明,这些函数必须要比 c stdio 垂直线的函数慢,但这个事实会使它们比使用 c stdio 函数正常慢一些(我曾经用 gcc/libstdc++ 进行基准测试过,确实注意到了一些减速,但你在日常使用中几乎不会注意到)。

3

当然,这是与实现相关的。

但如果你真的想知道,可以编写两个小程序并进行比较。你需要包含你所想要的典型用法,两个程序需要生成相同的字符串,然后使用分析器查看时间信息。

这样你就会知道了。


1

一个问题可能是由 ostringstream 添加的类型安全性会带来额外的开销。虽然我没有进行任何度量。


0
是的,如果您在Visual C++ 5.0上对几百万个数字运行下面的函数,第一个版本的运行时间大约是第二个版本的两倍,并且产生相同的输出。
将紧密循环编译为 .exe 并运行 Windows 的 'timethis something.exe' 或 Linux 的 'time something' 是我调查大部分性能问题的方式。('timethis' 在网上某处可获得)
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
你是否正在进行优化编译?如果没有,那么你的结果可能不太有趣 - 如果是这样,那么编译器很可能会优化为 void Hexx32Bit(...) {} - Eric

0

很可能是因为 sprintf 是由汇编语言编写的 CRT 的一部分。而 ostringstream 则是 STL 的一部分,可能更通用,并且需要处理面向对象的代码/开销。


4
我非常怀疑除了作为一种折磨自己的练习,没有人曾经用汇编语言实现过sprintf。早在UNIX V6时代,C运行时库就已经是用C语言编写的(除了一些不可能用C语言编写的极少数东西,比如setjmp)。 - zwol

0

正如litb所说,标准流支持我们并不总是需要的许多功能。 一些流实现摒弃了这种从未使用的灵活性,例如FAStream


FAStream发生了什么?我在论坛上看到一些回复使用它们,但现在我找不到任何相关信息。 - Javi
唉,我不知道。我认为最好直接向作者询问。 - Luc Hermitte
链接已失效。 - Eric

-1

我知道 printf 函数族比相应的 C++ 函数(cout、cin 和其他流)更快的一个原因是后者进行类型检查。由于这通常涉及一些对重载运算符的请求,因此可能需要一些时间。

事实上,在编程竞赛中,通常建议您使用 printf 等函数,而不是 cout/cin,正是出于这个原因。


4
这种类型检查以及运算符重载都是在编译时解决的。它们并不会导致运行时性能下降。 - Pyry Jahkola
4
事实上,printf()家族需要在运行时解析格式,这与观察结果不匹配。 - MSalters
2
C++类型检查不会产生运行时成本。除非你在谈论编译速度,在这种情况下,<iostream>很糟糕。 - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接