輝夜's profileKaguya Houraisan's glori...PhotosBlogListsMore Tools Help

Blog


    October, 2009

    File I/O 效率 C vs C++ (一)

    继续关注文件读写...这次测试写的效率

    其实关于这个问题的讨论似乎总会以类似语言信仰问题而告终,再加 C++ IO 库的复杂性,很多半调子 C++ 程序员总会出现各种误用,反过来却作为攻击 C++ IO 效率低的凭证。
    比如经常有人边在输出时大量使用类似
        fout<<...<<endl;
    的语句边嚷嚷着写文件速度超慢的,只能说这些人根本不知道自己写的句子都做了什么...
     
    所以说在评价任何东西之前先要做到最起码的了解
    咱也算是大致研究过 C++ IO stream 各方面的内容,虽然不能说完全掌握仍在学习中,但自认为还是可以写点东西的。
     
    总之还是用数据说话。
    平台 XPsp3 + VC2008 Express Edition SP1 + STLport / MinGW(GCC4.4.0)
     
    实验内容:
    共做了三种类型写入的比较,每种类型采用几种不同方法实现:
    1. 纯字节流写入
         (1) C fputs()
         (2) C fprintf()
         (3) C++ ofstream<<
         (4) C++ ofstream.rdbuf()->sputn()
    2. 宽字符流通过转码写入
         (1) C++ locale + wofstream<<
         (2) C++ codecvt<> facet + ofstream.rdbuf()->sputn()
         (3) WinAPI WideCharToMultiByte() + ofstream<<
    3. 格式化写入 (一个 int 一个 double + 一个字符串)
         (1) C fprintf()
         (2) C++ ofstream<<
         (3) C++ ostream.rdbuf()->sputn() sputc() + num_put<> facet
     
    前两类比较时写入的内容是完全一致的,也可以横向做下参照。
     
    结果:(由于和硬盘寻道时间等也有关系,尽量多次测试取受影响最小的值)
    以 C Runtime library 的 fputs 函数所用时间为 1.0 做基准

    VC2008EE + VC STL:
    text out C fputs()                      1.0
    text out C fprintf()                    2.0
    text out C++ ofstream <<                1.2
    text out C++ ofstream rdbuf()->sputn()  0.8
    wText out C++ wofstream deflocale      25.0
    wText out C++ locale codecvt            7.5
    wText out C++ ofstream + WinAPI         1.5
    Format data out C fprintf()             2.0
    Format data out C++ ofstream  <<        4.0
    Format data out C++ ofs num_put facet   3.0
     
    首先用 VC 自带的标准库,可以看到直接用 C++ fstream 的 << 效率与 C 库函数相比还是略低一些,但差距并没有到不可忍受的地步,考虑到 C++ IO stream 的各种特性,这些还是可以接受的。
    而直接调用下层 rdbuf()->sputn() 函数却比 fputs() 效率更高,让我对 C++ IO 库的进一步优化仍有希望。
    在 Unicode 大行其道的今天,宽字符的读写已成为必需,但使用 locale 配合 wstream 来做的话效率确实无法接受,从上面看居然比调用 WinAPI 的方法慢几十倍。我还是严重怀疑 VC STL 的实现有问题,只是换做直接调用 codecvt<> facet 就能提高好几倍效率。
    格式化读写一向是 C++ IO stream 被重点诟病的地方,与 fprintf() 函数整相差一倍,即使直接调用 num_put (去掉了构造 ios_base::sentry 对象产生的流同步、空白跳过等操作),仍然有 50% 的差距。
    其实从理论上来说,C++ 的格式化读写应当是比 C 的 fprintf() 函数要快的,因为 fprintf() 总要有一个解析格式字符串的过程,这个只能放在运行时,而 C++ 的格式是通过多个连续函数调用控制的,可以在编译时即进行优化。但实践往往存在各种变数 = =
     
    VC2008EE + STLport5.2.1:
    text out C fputs()                      1.0
    text out C fprintf()                    2.0
    text out C++ ofstream <<                0.7
    text out C++ ofstream rdbuf()->sputn()  0.6
    wText out C++ wofstream deflocale       7.5
    wText out C++ locale codecvt            7.0
    wText out C++ ofstream + WinAPI         1.0
    Format data out C fprintf()             2.0
    Format data out C++ ofstream  <<        2.0
    Format data out C++ ofs num_put facet   1.7
     
    STLport 的 C 库函数完全是照搬 VC 的标准运行库,而只是重新实现了整个 C++ 库,所以 C 函数的效率与上一例是相同的,可直接横向比较。
    面对这样的结果,我只能说 STLport 太赞了!!再考虑到其他的种种特性,赶紧扔掉 VC STL 全部换用 STLport 吧
    不过转码看来确实是一个平台相关的特性,仍然无法比过 WinAPI 的效率

    MinGW GCC4.4.0 + libstdc++:
    text out C fputs()                      1.5
    text out C fprintf()                    2.7
    text out C++ ofstream <<                6.0
    text out C++ ofstream rdbuf()->sputn()  2.7
    mingw libstdc++ doesnot support other locale...
    mingw libstdc++ doesnot support other locale...
    wText out C++ ofstream + WinAPI         2.1
    Format data out C fprintf()             3.0
    Format data out C++ ofstream  <<        6.6
    Format data out C++ ofs num_put facet   5.8
     
    MinGW 貌似比较慢,是因为 MinGW 默认输出按 UTF-8 编码,对中文来说,字节数是 ANSI 的 1.5 倍。只有调用 API 的那个例子保持了 ANSI 编码。
    除以 1.5 的话可以看到 C 库函数的效率与 VC 差不多,但 C++ 的效率比 VC 略低,与 STLport 比就差更远了。毕竟 GCC + libstdc++ 在 Win 平台不是原生支持,只作为跨平台的特性这样也足够了。
    没有用 MinGW + STLport 做实验,不知能不能达到 VC 的效率。
     
    -------------------------------------------------------------
    以前使用 C++ 的 IO stream 做输入输出时总担心效率问题,现在有了 STLport 做支持就可以放心大胆的用了。
    但可以看到 C++ 的流式 IO 非常依赖于库实现,在各平台上的表现大概不如 C 库函数来得稳定。
    而且使用除 "C" 之外的 locale 时效率确实还是有问题,转码的话还是直接调用平台 API 省时省力又高效。MinGW 考虑不引入 locale 部分也是有道理的啊...
     
    这次全是 O,下次再比较 I 的情况...
     
    最后按惯例是程序代码:
    C++语言:
    001 // test the performance of C I/O & C++ streams & C++ codecvt facet
    002 #include<cstdio>
    003 #include<iostream>
    004 #include<fstream>
    005 #include<locale>
    006 #include<cstdlib>
    007 #include<ctime>
    008 #include<windows.h>
    009 using namespace std;
    010
    011 const int testsize = 50000;
    012
    013 int main(){
    014     char cstr[] = "这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串";
    015     wchar_t wstr[] = L"这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串这是实验字符串";
    016     char buffer[200];
    017     int cstrlen = strlen(cstr);
    018     locale defloc("");
    019
    020     clock_t start;
    021     FILE *cfile;
    022     ofstream fout;
    023     wofstream wfout;
    024
    025     // pure text ...........................
    026     start = clock();
    027     cfile = fopen("text_out_C_fputs.txt", "w");
    028     for(int i = 0; i < testsize; ++i){
    029         fputs(cstr, cfile);
    030         fputc('\n', cfile);
    031     }
    032     fclose(cfile);
    033     printf("Text out C fputs: %d\n", clock()-start);
    034
    035     start = clock();
    036     cfile = fopen("test_out_C_fprintf.txt", "w");
    037     for(int i = 0; i < testsize; ++i){
    038         fprintf(cfile, "%s\n", cstr);
    039     }
    040     fclose(cfile);
    041     printf("Text out C fprintf: %d\n", clock()-start);
    042
    043     fout.clear();
    044     start = clock();
    045     fout.open("text_out_Cpp_ofstream.txt");
    046     for(int i = 0; i < testsize; ++i){
    047         fout << cstr << '\n';
    048     }
    049     fout.close();
    050     printf("Text out Cpp ofstream: %d\n", clock()-start);
    051
    052     fout.clear();
    053     start = clock();
    054     fout.open("text_out_Cpp_rdbuf.txt");
    055     for(int i = 0; i < testsize; ++i){
    056         fout.rdbuf()->sputn(cstr, cstrlen);
    057         fout.rdbuf()->sputc('\n');
    058     }
    059     fout.close();
    060     printf("Text out Cpp rdbuf: %d\n", clock()-start);
    061
    062     // wchar_t text ...............................
    063     wfout.clear();
    064     start = clock();
    065     wfout.open("wtext_out_Cpp_wofs_defloc.txt");
    066     wfout.imbue(defloc);
    067     for(int i = 0; i < testsize; ++i){
    068         wfout << wstr << L'\n';
    069     }
    070     wfout.close();
    071     printf("wText out Cpp wofs with default locale: %d\n",
    072         clock()-start);
    073
    074     fout.clear();
    075     start = clock();
    076     char *next;
    077     const wchar_t *wnext;
    078     mbstate_t st;
    079     fout.open("wtext_out_Cpp_codecvt_facet.txt");
    080     for(int i = 0; i < testsize; ++i){
    081         use_facet<codecvt<wchar_t, char, mbstate_t> >(defloc).out(
    082             st, wstr, wstr+sizeof(wstr)/2-1, wnext,
    083             buffer, buffer+sizeof(buffer)-1, next);
    084         fout.rdbuf()->sputn(buffer, next-buffer);
    085         fout.rdbuf()->sputc('\n');
    086     }
    087     fout.close();
    088     printf("wText out Cpp ofs with codecvt facet: %d\n",
    089         clock()-start);
    090
    091     fout.clear();
    092     start = clock();
    093     fout.open("wtext_out_Cpp_ofs_WinAPI.txt");
    094     for(int i = 0; i < testsize; ++i){
    095         WideCharToMultiByte(CP_ACP, 0, wstr, -1, buffer, 200,
    096             NULL, NULL);
    097         fout << buffer << '\n';
    098     }
    099     fout.close();
    100     printf("wText out Cpp ofs with WideCharToMultiByte API: %d\n",
    101         clock()-start);
    102
    103     // Format out ...........................................
    104     srand((unsigned)time(NULL));
    105
    106     char datastr[] = "TestDataString实验格式化字符串";
    107     start = clock();
    108     cfile = fopen("format_data_out_C_fprintf.txt", "w");
    109     for(int i = 0; i < testsize; ++i){
    110         fprintf(cfile, "%d %lf %s\n", rand(),
    111             double(rand())/RAND_MAX, datastr);
    112     }
    113     fclose(cfile);
    114     printf("Format data out C fprintf: %d\n", clock()-start);
    115
    116     fout.clear();
    117     start = clock();
    118     fout.open("format_data_out_Cpp_ofstream.txt");
    119     for(int i = 0; i < testsize; ++i){
    120         fout << rand() << ' ' << double(rand())/RAND_MAX << ' '
    121             << datastr << '\n';
    122     }
    123     fout.close();
    124     printf("Format data out Cpp ofstream: %d\n", clock()-start);
    125
    126     fout.clear();
    127     start = clock();
    128     fout.open("format_data_out_Cpp_ofs_facet.txt");
    129     for(int i = 0; i < testsize; ++i){
    130         use_facet<num_put<char> >(locale::classic()).put(
    131             ostreambuf_iterator<char>(fout), fout, ' ', (long)rand());
    132         fout.rdbuf()->sputc(' ');
    133         use_facet<num_put<char> >(locale::classic()).put(
    134             ostreambuf_iterator<char>(fout), fout, ' ',
    135             double(rand())/RAND_MAX);
    136         fout.rdbuf()->sputc(' ');
    137         fout.rdbuf()->sputn(datastr, sizeof(datastr) - 1);
    138         fout.rdbuf()->sputc('\n');
    139     }
    140     fout.close();
    141     printf("Format data out Cpp ofs with facet: %d\n", clock()-start);
    142
    143     system("pause");
    144 }

    Comments

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    Trackbacks

    The trackback URL for this entry is:
    http://dantvt.spaces.live.com/blog/cns!D87988A6CAC0A480!1019.trak
    Weblogs that reference this entry
    • None