在http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly的50:40安德烈亚历山大夫斯库开了一个关于如何效率/慢速istream的笑话。
过去我遇到了一个问题,ostream速度慢,fwrite显着更快(在主循环运行一次时减少了很多秒)但是我从来不理解为什么也没看过它。
是什么让istream和ostream在C ++中变慢?或者至少比其他东西(如fread / fget,fwrite)慢,这同样满足了需求。
答案 0 :(得分:43)
实际上,IOStreams不一定要慢!然而,这是以合理的方式实现它们以使它们快速的问题。大多数标准C ++库似乎没有过多关注实现IOStreams。很久以前,当我的CXXRT仍然保持时,它与stdio一样快 - 正确使用时!
请注意,使用IOStreams的用户几乎没有性能陷阱。以下指南适用于所有IOStream实现,尤其适用于那些为快速定制的实现:
std::cin
,std::cout
等时,您需要致电std::sync_with_stdio(false)
!如果没有此调用,则需要使用标准流对象来与C的标准流同步。当然,在使用std::sync_with_stdio(false)
时,我们假设您不会将std::cin
与stdin
,std::cout
与stdout
混合等等。std::endl
因为它要求对任何缓冲区进行许多不必要的刷新。同样,请勿设置std::ios_base::unitbuf
或不必要地使用std::flush
。virtual
函数,这会使它变得非常慢。答案 1 :(得分:9)
也许这可以让你知道你正在处理什么:
#include <stdio.h>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <fstream>
#include <time.h>
#include <string>
#include <algorithm>
unsigned count1(FILE *infile, char c) {
int ch;
unsigned count = 0;
while (EOF != (ch=getc(infile)))
if (ch == c)
++count;
return count;
}
unsigned int count2(FILE *infile, char c) {
static char buffer[8192];
int size;
unsigned int count = 0;
while (0 < (size = fread(buffer, 1, sizeof(buffer), infile)))
for (int i=0; i<size; i++)
if (buffer[i] == c)
++count;
return count;
}
unsigned count3(std::istream &infile, char c) {
return std::count(std::istreambuf_iterator<char>(infile),
std::istreambuf_iterator<char>(), c);
}
unsigned count4(std::istream &infile, char c) {
return std::count(std::istream_iterator<char>(infile),
std::istream_iterator<char>(), c);
}
unsigned int count5(std::istream &infile, char c) {
static char buffer[8192];
unsigned int count = 0;
while (infile.read(buffer, sizeof(buffer)))
count += std::count(buffer, buffer+infile.gcount(), c);
count += std::count(buffer, buffer+infile.gcount(), c);
return count;
}
unsigned count6(std::istream &infile, char c) {
unsigned int count = 0;
char ch;
while (infile >> ch)
if (ch == c)
++count;
return count;
}
template <class F, class T>
void timer(F f, T &t, std::string const &title) {
unsigned count;
clock_t start = clock();
count = f(t, 'N');
clock_t stop = clock();
std::cout << std::left << std::setw(30) << title << "\tCount: " << count;
std::cout << "\tTime: " << double(stop-start)/CLOCKS_PER_SEC << "\n";
}
int main() {
char const *name = "equivs2.txt";
FILE *infile=fopen(name, "r");
timer(count1, infile, "ignore");
rewind(infile);
timer(count1, infile, "using getc");
rewind(infile);
timer(count2, infile, "using fread");
fclose(infile);
std::ifstream in2(name);
timer(count3, in2, "ignore");
in2.clear();
in2.seekg(0);
timer(count3, in2, "using streambuf iterators");
in2.clear();
in2.seekg(0);
timer(count4, in2, "using stream iterators");
in2.clear();
in2.seekg(0);
timer(count5, in2, "using istream::read");
in2.clear();
in2.seekg(0);
timer(count6, in2, "using operator>>");
return 0;
}
运行这个,我得到这样的结果(使用MS VC ++):
ignore Count: 1300 Time: 0.309
using getc Count: 1300 Time: 0.308
using fread Count: 1300 Time: 0.028
ignore Count: 1300 Time: 0.091
using streambuf iterators Count: 1300 Time: 0.091
using stream iterators Count: 1300 Time: 0.613
using istream::read Count: 1300 Time: 0.028
using operator>> Count: 1300 Time: 0.619
和这个(与MinGW):
ignore Count: 1300 Time: 0.052
using getc Count: 1300 Time: 0.044
using fread Count: 1300 Time: 0.036
ignore Count: 1300 Time: 0.068
using streambuf iterators Count: 1300 Time: 0.068
using stream iterators Count: 1300 Time: 0.131
using istream::read Count: 1300 Time: 0.037
using operator>> Count: 1300 Time: 0.121
正如我们在结果中所看到的,这并不是说iostream明显缓慢。相反,很大程度上取决于你如何使用iostream(以及在较小程度上FILE *
)。这些与实现之间也存在相当大的差异。
尽管如此,每个版本(fread
和istream::read
)的最快版本基本上都是绑定的。使用VC ++ getc
比istream::read
或istreambuf_iterator
慢一点。
结论:从iostreams获得良好的性能需要比FILE *
更多的关注 - 但它肯定是可能的。它们还为您提供了更多选择:当您不关心速度时的便利性,以及与C风格I / O最佳匹配的性能,以及一些额外的工作。
答案 2 :(得分:7)
[i]流在设计上变慢的原因有很多:
共享的格式化状态:每个格式化的输出操作都必须检查以前可能已被I / O操作器更改的所有格式化状态。因此,iostream本质上比所有格式信息都是本地的类似printf
的API(特别是像Rust或{fmt}这样的格式字符串编译,避免了解析开销)要慢。
对语言环境的不受控制的使用:即使您不希望这样做,例如在编写JSON文件时,所有格式都会经过低效的语言环境层。参见N4412: Shortcomings of iostreams。
低效率的代码生成:使用iostream格式化消息通常包含多个函数调用,因为参数和I / O操纵器与消息的某些部分交织在一起。例如,在
中有三个函数调用(godbolt)std::cout << "The answer is " << answer << ".\n";
相比于等效的printf
通话中只有一个(godbolt):
printf("The answer is %d.\n", answer);
额外的缓冲和同步。可以使用sync_with_stdio(false)
禁用此功能,但代价是与其他I / O设施的互操作性较差。
答案 3 :(得分:0)
在类似的主题上,STL说:&#34;你可以调用setvbuf()来启用stdout上的缓冲。&#34;
答案 4 :(得分:0)
虽然这个问题很老,但我很惊讶没有人提到过iostream对象的构建。
也就是说,无论何时创建STL iostream
(和其他流变体),如果您单步执行代码,构造函数将调用内部Init
函数。在那里,调用operator new
来创建一个新的locale
对象。
同样,在毁灭时被摧毁。
这很可怕,恕我直言。并且肯定会导致缓慢的对象构造/破坏,因为在某些时候使用系统锁分配/释放内存。
此外,一些STL流允许您指定allocator
,那么为什么创建的locale
不使用指定的分配器?
在多线程环境中使用流,您还可以想象每次构造新流对象时调用operator new
所造成的瓶颈。