为什么istream / ostream变慢

问题描述 投票:33回答:4

http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly 50:40安德烈亚历山大夫斯库开了一个关于如何效率/慢速istream的笑话。

我过去遇到过一个问题,ostream很慢,并且fwrite明显更快(在主循环运行一次时减少了很多秒)但是我从来不理解为什么也没看过它。

什么使得C ++中的istream和ostream变慢?或者至少比其他东西(如fread / fget,fwrite)慢,这同样满足了需求。

c++ performance ostream istream
4个回答
43
投票

实际上,IOStreams不一定要慢!然而,这是以合理的方式实现它们以使它们快速的问题。大多数标准C ++库似乎没有过多关注实现IOStreams。很久以前,当我的CXXRT仍然保持时,它与stdio一样快 - 正确使用时!

请注意,使用IOStreams的用户几乎没有性能陷阱。以下指南适用于所有IOStream实现,尤其适用于那些快速定制的实现:

  1. 当使用std::cinstd::cout等时,你需要打电话给std::sync_with_stdio(false)!如果没有此调用,则需要使用标准流对象来与C的标准流同步。当然,当使用std::sync_with_stdio(false)时,假设你没有将std::cinstdin混合,std::coutstdout等。
  2. Do not use std::endl因为它要求任何缓冲区进行许多不必要的刷新。同样,不要设置std::ios_base::unitbuf或不必要地使用std::flush
  3. 在创建自己的流缓冲区时(好的,很少有用户),确保它们使用内部缓冲区!处理单个字符会跳过多个条件和virtual函数,这使得它变得非常慢。

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明显缓慢。相反,很大程度上取决于你如何使用iostreams(以及较小程度上的FILE *)。这些实现之间也存在相当大的差异。

尽管如此,每个版本(freadistream::read)的最快版本基本上是并列的。使用VC ++ getcistream::readistreambuf_iterator慢一点。

一句话:从iostreams获得良好的性能需要比FILE *更多的关注 - 但它肯定是可能的。它们还为您提供了更多选择:当您不关心速度时的便利性,以及与C风格I / O最佳匹配的性能,以及一些额外的工作。


0
投票

虽然这个问题很老,但我很惊讶没有人提到过iostream对象的构造。

也就是说,无论何时创建STL iostream(和其他流变体),如果您单步执行代码,构造函数将调用内部Init函数。在那里,operator new被称为创建一个新的locale对象。同样,在毁灭时被毁灭。

这很可怕,恕我直言。并且肯定会导致缓慢的对象构造/破坏,因为在某些时候使用系统锁分配/释放内存。

此外,一些STL流允许您指定allocator,那么为什么locale创建不使用指定的分配器?

在多线程环境中使用流,您还可以想象每次构造新流对象时调用operator new所造成的瓶颈。

如果你问我,就像我现在发现自己一样糟透了!


© www.soinside.com 2019 - 2024. All rights reserved.