如何让IOStream表现得更好? P.大多数C++学习者在使用C++语言编写代码时,更喜欢使用 PROTFF />代码> SCANF函数族。

如何让IOStream表现得更好? P.大多数C++学习者在使用C++语言编写代码时,更喜欢使用 PROTFF />代码> SCANF函数族。,c++,optimization,iostream,c++-faq,c++-standard-library,C++,Optimization,Iostream,C++ Faq,C++ Standard Library,虽然我承认我发现接口方式更好(特别是POSIX格式和本地化),但似乎一个压倒性的问题是性能 看看这个问题: pubstebuf(缓冲区,大小)) 同步(std::ios\u base::与stdio同步) 区域设置处理(我们可以使用精简的区域设置,还是完全删除它?) 当然,也欢迎其他方法 注意:提到了Dietmar Kuhl的“新”实现,但我找不到关于它的很多细节。以前的引用似乎是死链接。有趣的是,你说C程序员在写C++时更喜欢Prtff,因为我看到很多代码是C,而不是使用 cOUT 和 i

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

看看这个问题:

<最好的答案是使用<代码> fSCANF,并且C++ <代码> IFSturi是慢速2-3倍。

我认为如果我们可以编译一个“提示”库来提高IOStreams的性能,哪些有效,哪些无效,那将是一件非常棒的事情

考虑的要点

  • 缓冲(
    rdbuf()->pubstebuf(缓冲区,大小)
  • 同步(
    std::ios\u base::与stdio同步
  • 区域设置处理(我们可以使用精简的区域设置,还是完全删除它?)
当然,也欢迎其他方法


注意:提到了Dietmar Kuhl的“新”实现,但我找不到关于它的很多细节。以前的引用似乎是死链接。有趣的是,你说C程序员在写C++时更喜欢Prtff,因为我看到很多代码是C,而不是使用<代码> cOUT <代码>和<代码> iSooS写输出。 使用通常可以通过直接使用
filebuf
获得更好的性能(Scott Meyers在有效的STL中提到了这一点),但是使用filebuf direct的文档相对较少,大多数开发人员更喜欢
std::getline
,这在大多数情况下更简单

关于区域设置,如果您创建了facet,您通常会获得更好的性能,方法是使用所有facet创建一次区域设置,保存它,并将其嵌入到您使用的每个流中


最近我确实在这里看到了关于这个的另一个主题,所以这几乎是一个重复。

以下是我到目前为止收集的内容:

缓冲

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

  • 它减少了硬盘的点击次数
  • 它减少了系统调用的数量
可以通过访问底层的
streambuf
实现来设置缓冲区

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
@iavr提供的警告:根据建议,最好在打开文件之前致电
pubsetbuf
。不同的标准库实现具有不同的行为

区域设置处理:

区域设置可以执行字符转换、过滤,以及涉及数字或日期的更巧妙的技巧。它们经历了一个复杂的动态调度和虚拟调用系统,因此删除它们有助于减少惩罚

默认的
C
区域设置意味着不执行任何转换,并且在机器之间是统一的。这是一个很好的默认值

同步:

我看不到使用此功能有任何性能改进

您可以使用
sync_with_stdio
静态功能访问全局设置(std::ios_base的静态成员)

测量值:

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

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

这意味着大约
20%
。。。对于默认代码。实际上,篡改缓冲区(在C或C++中)或同步参数(C++中)并没有产生任何改进

其他方面的结果:

@Irfy在g++4.7.2-2ubuntu1,-O3上,虚拟化Ubuntu 11.10,3.5.0-25-generic,x86_64,足够的ram/cpu,196MB的几个“find/>>largefile.txt”运行

C:634572 C++:473222

C++快25%

@g++4.4.5,-O3上的Matteo Italia,Ubuntu Linux 10.10 x86_64,带有一个180 MB的随机文件

C:910390
C++:776016

C++快17%

@Bogatyr基于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

C++111%慢

@clang++3.8.0-2ubuntu4上的Asu,Kubuntu 16.04 Linux 4.8-rc3,8GB ram,i5 Haswell,关键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);

    // 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;
}
#包括
#包括
#包括
#包括
#包括
#包括
模板
双基准(Func f,size\u t迭代)
{
f();
时间值a,b;
gettimeofday(&a,0);
对于(;迭代-->0;)
{
f();
}
gettimeofday(&b,0);
返回(b.tv_sec*(未签名整数)1e6+b.tv_usec)-
(a.tv_sec*(无符号整数)1e6+a.tv_usec);
}
结构CRead
{
CRead(char const*filename):_filename(filename){}
void运算符()(){
FILE*FILE=fopen(_文件名,“r”);
整数计数=0;
而(fscanf(文件,'%s',_buffer)==1){++count;}
fclose(文件);
}
char const*_文件名;
字符缓冲区[1024];
};
结构CppRead
{
CppRead(char const*filename):_filename(filename),_buffer(){}
枚举{BufferSize=16184};
void运算符()(){
std::ifstream文件(_文件名,std::ifstream::in);
//删除扩展缓冲区的注释
file.rdbuf()->pubstebuf(_buffer,BufferSize);
整数计数=0;
std::字符串s;
而(文件>>s){++count;}
}
char const*_文件名;
字符缓冲区[BufferSize];
};
int main(int argc,char*argv[])
{
迭代次数的大小=1;
如果(argc>1){iterations=atoi(argv[1]);}
char const*oldLocale=setlocale(LC_ALL,“C”);
if(strcmp(oldLocale,“C”)!=0){
std::cout还有两项改进:

问题
std::cin.tie(
#include <iostream>
using namespace std;

int main()
{
  ios_base::sync_with_stdio(false);

  int i;
  while(cin >> i)
    cout << i << '\n';
}
#include <iostream>
using namespace std;

int main()
{
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);

  int i;
  while(cin >> i)
    cout << i << '\n';

  cout.flush();
}
work@mg-K54C ~ $ time ./test1 < test.in > test1.in

real    0m3.140s
user    0m0.581s
sys 0m2.560s
work@mg-K54C ~ $ time ./test2 < test.in > test2.in

real    0m0.234s
user    0m0.234s
sys 0m0.000s
#include <iostream>
using namespace std;

int main()
{
  ios_base::sync_with_stdio(false);

  for(int i = 0; i < 1179648; ++i)
    cout << i << endl;
}
#include <iostream>
using namespace std;

int main()
{
  ios_base::sync_with_stdio(false);

  for(int i = 0; i < 1179648; ++i)
    cout << i << '\n';
}
work@mg-K54C ~ $ time ./test1 > test1.in

real    0m2.946s
user    0m0.404s
sys 0m2.543s
work@mg-K54C ~ $ time ./test2 > test2.in

real    0m0.156s
user    0m0.135s
sys 0m0.020s