答案 0 :(得分:47)
没有像标题那样回答你的问题的具体细节:2006 Technical Report on C++ Performance有一个关于IOStreams的有趣部分(第68页)。与您的问题最相关的是第6.1.2节(“执行速度”):
由于IOStreams处理的某些方面是 它分布在多个方面 似乎标准规定了一个 效率低下。但是这个 事实并非如此 - 通过使用某种形式 预处理,大部分工作都可以 要避免。稍微聪明一些 通常使用的链接器,它是 可以删除其中的一些 效率低下。这将在讨论中 §6.2.3和§6.2.5。
由于该报告是在2006年编写的,人们希望许多建议能够被纳入当前的编译器中,但也许情况并非如此。
正如您所提到的,write()
中的方面可能没有特征(但我不会盲目地假设)。那么功能是什么?在使用GCC编译的ostringstream
代码上运行GProf会产生以下细分:
std::basic_streambuf<char>::xsputn(char const*, int)
std::ostream::write(char const*, int)
main
std::ostream::sentry::sentry(std::ostream&)
std::string::_M_replace_safe(unsigned int, unsigned int, char const*, unsigned int)
std::basic_ostringstream<char>::basic_ostringstream(std::_Ios_Openmode)
std::fpos<int>::fpos(long long)
因此,大部分时间花在xsputn
上,在大量检查和更新光标位置和缓冲区之后最终调用std::copy()
(详情请查看c++\bits\streambuf.tcc
)。
我对此的看法是,你专注于最坏情况。如果您处理的是相当大的数据块,那么所执行的所有检查都只是完成的总工作量的一小部分。但是您的代码一次只能以四个字节的速度移动数据,并且每次都会产生所有额外成本。显然,人们会避免在现实生活中这样做 - 考虑如果在一个1m整数的数组上调用write
而不是在一个int上调用1m次,那么惩罚是多么微不足道。在现实生活中,人们会非常欣赏IOStream的重要特性,即内存安全和类型安全设计。这样的好处是有代价的,你编写了一个测试,使这些成本占据执行时间。
答案 1 :(得分:27)
我对Visual Studio用户感到非常失望,他们更喜欢这个用户:
ostream
的Visual Studio实现中,sentry
对象(标准所需)进入保护streambuf
的关键部分(这不是必需的)。这似乎不是可选的,因此即使对于单个线程使用的本地流,也需要支付线程同步的成本,而不需要同步。这会伤害使用ostringstream
严格格式化邮件的代码。使用stringbuf
直接避免使用sentry
,但格式化的插入运算符无法直接在streambuf
上运行。对于Visual C ++ 2010,关键部分将ostringstream::write
与基础stringbuf::sputn
调用相比减慢了三倍。
看看beldaz's profiler data on newlib,似乎很明显gcc的sentry
并没有像这样疯狂。 gcc下的ostringstream::write
仅比stringbuf::sputn
长约50%,但stringbuf
本身比VC ++慢得多。并且两者仍然比较使用vector<char>
进行I / O缓冲非常不利,尽管与VC ++下的边距不同。
答案 2 :(得分:8)
你看到的问题是每次调用write()时的开销。你添加的每个抽象级别(char [] - &gt; vector - &gt; string - &gt; ostringstream)都会增加一些函数调用/返回和其他内务处理guff,如果你称之为一百万次 - 加起来。 / p>
我修改了ideone上的两个示例,一次写入十个整数。 ostringstream时间从53到6毫秒(几乎提高了10倍),而char循环改进了(3.7到1.5) - 很有用,但只有两倍。
如果您担心性能,那么您需要为工作选择合适的工具。 ostringstream是有用且灵活的,但是按照你想要的方式使用它会受到惩罚。 char []是更难的工作,但性能提升可能很大(记住gcc可能也会为你编写memcpys)。
简而言之,ostringstream没有被破坏,但是越接近金属,代码运行得越快。汇编者对某些人来说仍然有优势。
答案 3 :(得分:1)
要获得更好的性能,您必须了解您使用的容器的工作方式。在char []数组示例中,预先分配所需大小的数组。在vector和ostringstream示例中,随着对象的增长,您迫使对象重复分配和重新分配并可能多次复制数据。
使用std :: vector,可以通过将矢量的大小初始化为最终大小来轻松解决,就像使用char数组一样;相反,你通过调整为零而不公平地削弱了性能!这不是一个公平的比较。
关于ostringstream,不可能预先分配空间,我建议使用它是不合适的。该类具有比简单char数组更大的实用程序,但如果您不需要该实用程序,则不要使用它,因为您将在任何情况下支付开销。相反,它应该用于它的好处 - 将数据格式化为字符串。 C ++提供了各种各样的容器,而ostringstram是最不合适的容器。
在vector和ostringstream的情况下,你可以获得缓冲区溢出的保护,你不会得到一个char数组,并且这种保护不是免费的。