如何让IOStream执行更好? [英] How to get IOStream to perform better?

查看:150
本文介绍了如何让IOStream执行更好?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以前的C用户喜欢使用 printf / scanf 系列函数,甚至在C ++中。 p>

虽然我承认我发现接口的方式更好(特别是类似POSIX的格式和本地化),但似乎压倒性的关注是性能。



看看他的问题:


如何加快逐行读取文件的速度


似乎最好的答案是使用 fscanf ... C ++ ifstream 始终是x2-x3倍慢。



我认为如果我们可以编译



反射的镜头是:

ul>
  • 缓冲( rdbuf() - > pubsetbuf(buffer,size)?)

  • std :: ios_base :: sync_with_stdio ?)

  • 区域设置(我们可以使用缩小的区域设置吗?全部删除?)



  • 当然,欢迎其他方法。



    注意:由Dietmar Kuhl提到的一个新实现,但我无法找到关于它的更多细节,以前的引用似乎是一个死链接

    解决方案

    这是我到目前为止收集的:



    缓冲



    如果默认情况下缓冲区非常小,增加缓冲区大小肯定可以提高性能:




    • 它减少了硬盘命中次数

    • 它减少了系统调用次数



    可以通过访问底层的 streambuf 实现来设置缓冲区。

      char Buffer [N]; 

    std :: ifstream file(file.txt);

    file.rdbuf() - > pubsetbuf(Buffer,N);
    // rdbuf的指针读取器保证
    //成功构造函数后成为非null


    b $ b

    警告由@iavr提供:根据 cppreference ,最好在打开文件之前调用 pubsetbuf 。各种标准库实现的行为有所不同。



    区域设置



    Locale可以执行字符转换,过滤,以及涉及数字或日期的更聪明的技巧。他们经历了一个复杂的动态调度和虚拟调用系统,因此删除它们可以帮助减少罚分。



    默认 C locale意味着不执行任何转换以及跨机器统一。



    同步:



    我看不到

    可以访问全局设置( std :: ios_base 的静态成员)



    code $> code 测量:



    使用这个,我玩弄了一个简单的程序,使用 gcc 3.4.2 在SUSE 10p3上编译 -O2


    C:7.76532e + 06

    C ++:1.0874e + 07


    这代表了减少大约 20% ...用于默认代码。实际上,篡改缓冲区(在C或C ++中)或同步参数(C ++)没有产生任何改善。



    其他人的结果:


    @Irfy on g ++ 4.7.2-2ubuntu1,-O3,虚拟化Ubuntu 11.10,3.5.0-25通用,x86_64,足够ram / cpu,196MB几个find / >> largefile.txt运行



    C:634572
    C ++:473222


    C ++ 速度快25%



    > b $ b

    @ gatteo Italia on g ++ 4.4.5 ,-O3,Ubuntu Linux 10.10 x86_64和随机的180 MB文件



    C:910390

    C ++:776016


    C ++ 速度快17%


    @ Bogatyr on g ++ i686-apple-darwin10-g ++ - 4.2.1(GCC)4.2.1(Apple Inc. build 5664),mac mini,4GB ram,空闲,除了此测试带有168MB数据文件



    C:4.34151e + 06

    C ++:9.14476e + 06




    < 111%较慢


    @Asu on clang ++ 3.8.0-2ubuntu4,Kubuntu 16.04 Linux 4.8-rc3,8GB ram,i5 Haswell,Crucial SSD,88MB数据文件(tar.xz存档)



    C:270895
    C ++:162799


    C ++ 更快速66%



    因此答案是:问题,真的取决于平台:/



    这里的代码对于那些有兴趣的基准测试:

      #include< fstream> 
    #include< iostream>
    #include< iomanip>

    #include< cmath>
    #include< cstdio>

    #include< sys / time.h>

    template< typename Func>
    double benchmark(Func f,size_t iterations)
    {
    f();

    timeval a,b;
    gettimeofday(& a,0);
    for(; iterations - > 0;)
    {
    f();
    }
    gettimeofday(& b,0);
    return(b.tv_sec *(unsigned int)1e6 + b.tv_usec) -
    (a.tv_sec *(unsigned int)1e6 + a.tv_usec);
    }


    struct CRead
    {
    CRead(char const * filename):_filename(filename){}

    void operator()(){
    FILE * file = fopen(_filename,r);

    int count = 0;
    while(fscanf(file,%s,_buffer)== 1){++ count; }

    fclose(file);
    }

    char const * _filename;
    char _buffer [1024];
    };

    struct CppRead
    {
    CppRead(char const * filename):_filename(filename),_buffer(){}

    enum {BufferSize = 16184 };

    void operator()(){
    std :: ifstream file(_filename,std :: ifstream :: in);

    //注释删除扩展缓冲区
    file.rdbuf() - > pubsetbuf(_buffer,BufferSize);

    intcount = 0;
    std :: string s;
    while(file>> s){++ count; }
    }

    char const * _filename;
    char _buffer [BufferSize];
    };


    int main(int argc,char * argv [])
    {
    size_t iterations = 1;
    if(argc> 1){iterations = atoi(argv [1]); }

    char const * oldLocale = setlocale(LC_ALL,C);
    if(strcmp(oldLocale,C)!= 0){
    std :: cout< 替换旧地区< oldLocale< 'by'C'\\\
    ;
    }

    char const * filename =largefile.txt;

    CRead cread(filename);
    CppRead cppread(filename);

    //注释使用默认设置
    bool oldSyncSetting = std :: ios_base :: sync_with_stdio(false);

    double ctime = benchmark(cread,iterations);
    double cpptime = benchmark(cppread,iterations);

    //如果oldSyncSetting的声明被注释,则注释
    std :: ios_base :: sync_with_stdio(oldSyncSetting);

    std :: cout<< C:< ctime< \\\

    C ++:< cpptime<< \\\
    ;

    return 0;
    }


    Most previously C-users prefer to use the printf / scanf family of functions even in C++.

    Although I admit that I find the interface way better (especially POSIX-like format and localization), it seems that an overwhelming concern is performance.

    Taking at look at his question:

    How can I speed up line by line reading of a file

    It seems that the best answer is to use fscanf... and that the C++ ifstream is consistently x2-x3 times slower.

    I thought it would be great if we could compile a repository of "tips" to improve IOStreams performance: what work, what does not.

    Pists of reflexion are:

    • buffering (rdbuf()->pubsetbuf(buffer, size) ?)
    • synchronization (std::ios_base::sync_with_stdio ?)
    • locale handling (Could we use a trimmed-down locale ? Remove it altogether ?)

    And of course, other approaches are welcome.

    Note: an "new" implementation, by Dietmar Kuhl, was mentioned, but I was unable to locate much details about it, previous references seem to be a dead link

    解决方案

    Here is what I have gathered so far:

    Buffering:

    If by default the buffer is very small, increasing the buffer size can definitely improve the performance:

    • it reduces the number of HDD hits
    • it reduces the number of system calls

    Buffer can be set by accessing the underlying streambuf implementation.

    char Buffer[N];
    
    std::ifstream file("file.txt");
    
    file.rdbuf()->pubsetbuf(Buffer, N);
    // the pointer reader by rdbuf is guaranteed
    // to be non-null after successful constructor
    

    Warning courtesy of @iavr: according to cppreference it is best to call pubsetbuf before opening the file. Various standard library implementations otherwise have different behaviors.

    Locale Handling:

    Locale can perform character conversion, filtering, and more clever tricks where numbers or dates are involved. They go through a complex system of dynamic dispatch and virtual calls, so removing them can help trimming down the penalty hit.

    The default C locale is meant not to perform any conversion as well as being uniform across machines. It's a good default to use.

    Synchronization:

    I could not see any performance improvement using this facility.

    One can access a global setting (static member of std::ios_base) using the sync_with_stdio static function.

    Measurements:

    Playing with this, I have toyed with a simple program, compiled using gcc 3.4.2 on SUSE 10p3 with -O2.

    C : 7.76532e+06
    C++: 1.0874e+07

    Which represents a slowdown of about 20%... for the default code. Indeed tampering with the buffer (in either C or C++) or the synchronization parameters (C++) did not yield any improvement.

    Results by others:

    @Irfy on g++ 4.7.2-2ubuntu1, -O3, virtualized Ubuntu 11.10, 3.5.0-25-generic, x86_64, enough ram/cpu, 196MB of several "find / >> largefile.txt" runs

    C : 634572 C++: 473222

    C++ 25% faster

    @Matteo Italia on g++ 4.4.5, -O3, Ubuntu Linux 10.10 x86_64 with a random 180 MB file

    C : 910390
    C++: 776016

    C++ 17% faster

    @Bogatyr on g++ i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664), mac mini, 4GB ram, idle except for this test with a 168MB datafile

    C : 4.34151e+06
    C++: 9.14476e+06

    C++ 111% slower

    @Asu on clang++ 3.8.0-2ubuntu4, Kubuntu 16.04 Linux 4.8-rc3, 8GB ram, i5 Haswell, Crucial SSD, 88MB datafile (tar.xz archive)

    C : 270895 C++: 162799

    C++ 66% faster

    So the answer is: it's a quality of implementation issue, and really depends on the platform :/

    The code in full here for those interested in benchmarking:

    #include <fstream>
    #include <iostream>
    #include <iomanip>
    
    #include <cmath>
    #include <cstdio>
    
    #include <sys/time.h>
    
    template <typename Func>
    double benchmark(Func f, size_t iterations)
    {
      f();
    
      timeval a, b;
      gettimeofday(&a, 0);
      for (; iterations --> 0;)
      {
        f();
      }
      gettimeofday(&b, 0);
      return (b.tv_sec * (unsigned int)1e6 + b.tv_usec) -
             (a.tv_sec * (unsigned int)1e6 + a.tv_usec);
    }
    
    
    struct CRead
    {
      CRead(char const* filename): _filename(filename) {}
    
      void operator()() {
        FILE* file = fopen(_filename, "r");
    
        int count = 0;
        while ( fscanf(file,"%s", _buffer) == 1 ) { ++count; }
    
        fclose(file);
      }
    
      char const* _filename;
      char _buffer[1024];
    };
    
    struct CppRead
    {
      CppRead(char const* filename): _filename(filename), _buffer() {}
    
      enum { BufferSize = 16184 };
    
      void operator()() {
        std::ifstream file(_filename, std::ifstream::in);
    
        // comment to remove extended buffer
        file.rdbuf()->pubsetbuf(_buffer, BufferSize);
    
        int count = 0;
        std::string s;
        while ( file >> s ) { ++count; }
      }
    
      char const* _filename;
      char _buffer[BufferSize];
    };
    
    
    int main(int argc, char* argv[])
    {
      size_t iterations = 1;
      if (argc > 1) { iterations = atoi(argv[1]); }
    
      char const* oldLocale = setlocale(LC_ALL,"C");
      if (strcmp(oldLocale, "C") != 0) {
        std::cout << "Replaced old locale '" << oldLocale << "' by 'C'\n";
      }
    
      char const* filename = "largefile.txt";
    
      CRead cread(filename);
      CppRead cppread(filename);
    
      // comment to use the default setting
      bool oldSyncSetting = std::ios_base::sync_with_stdio(false);
    
      double ctime = benchmark(cread, iterations);
      double cpptime = benchmark(cppread, iterations);
    
      // comment if oldSyncSetting's declaration is commented
      std::ios_base::sync_with_stdio(oldSyncSetting);
    
      std::cout << "C  : " << ctime << "\n"
                   "C++: " << cpptime << "\n";
    
      return 0;
    }
    

    这篇关于如何让IOStream执行更好?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆