C++禁用同步的方式

描述

恰好之前研究过这块,所以借助本文,分析下这个问题(无意于语言之争,单纯是从技术角度)。

众所周知,C++兼容了C的所有功能,显然从所有角度去对比分析是不现实的,所以本文从我们常用的输入输出即标准流(iostream和stdio)的角度来分析讲解。

示例

为了更加直观地来对比分析,写了个示例,通过scanf和cin读文件,然后分析两种方式的性能高低,代码如下:

 

#include 
#include 
#include 
#include 
const int num=1000000;

void time_report(const std::function &f1, const std::function &f2) {
   auto start = std::now();
   f1();
   auto end = std::now();
   std::cout << "cin cost " << std::duration_cast(end - start).count() << "ms" << std::endl;

   start = std::now();
   f2();
   end = std::now();
   std::cout << "scanf cost " << std::duration_cast(end - start).count() << "ms" << std::endl;
}


void write() {
  std::ofstream file("./data");
  for(int i = 0; i < num; ++i) {
    file << rand() <<' ';
    if((i + 1) % 20 == 0) {
      file << std::endl;
    }
  }
}
int main() {
  write(); 
  
  time_report([](){
  freopen("./data","r",stdin);
  int n = 0;
  for(int i=0; i> n;
  }
}, [](){
  freopen("./data","r",stdin);
  int n = 0;
  for(int i = 0; i < num; ++i) {
     scanf("%d", &n);
  }
});

return 0;
}

 

编译,运行之后,输出如下:

 

cin cost 686ms
scanf cost 189ms

 

从上述输出来看,cin的耗时是scanf的3倍多,果真如此么?

sync_with_stdio

C++性能真的差C这么多吗?直接颠覆了对C++的认知,即使性能真的低,也得知道为什么低吧,于是开始研究,发现C++为了兼容C,在C标准流(stdio)和C++标准流(iostrem)保持同步,这样就可以混合使用C和C++风格的I/O,且能保证得到合理和预期的结果,而正是这个同步导致C++在cin性能上有损失。如果禁用同步,则允许C++流拥有自己的独立缓冲区,这样性能就会提升很多。

那么是否可以禁用该同步功能呢?

C++提供了一个函数std::sync_with_stdio,声明如下:

 

static bool sync_with_stdio(bool __sync = true);

 

如果参数为false,则代表禁用此同步。从上面声明可以看出,默认情况下 __sync = true也就是说禁用同步,而如果__sync为false的话,则会有如下操作:

 

bool
  ios_base::sync_with_stdio(bool __sync) {
    bool __ret = ios_base::_S_synced_with_stdio;
    
    if (!__sync && __ret) {
       // ...
       cout.rdbuf(&buf_cout);
       cin.rdbuf(&buf_cin);
       cerr.rdbuf(&buf_cerr);
       clog.rdbuf(&buf_cerr);
       // ...
    }
  return __ret;
}

 

从上述代码,进一步验证了我们上面的说法,如果禁用了同步功能,则C++流使用自己的缓冲区buf_cin(此处以cin为例),几种buffer的定义如下:

 

 typedef char fake_filebuf[sizeof(stdio_filebuf)]
  __attribute__ ((aligned(__alignof__(stdio_filebuf))));
  fake_filebuf buf_cout;
  fake_filebuf buf_cin;
  fake_filebuf buf_cerr;

 

好了,截止到现在,我们已经搞清楚了为什么C++流性能要慢于C,为了验证是否真的是因为使用了同步功能而导致的性能差异,使用std::sync_with_stdio(false)关闭同步,代码示例如下:

 


 

#include 

#include  #include  #include  const int num=1000000; void time_report(const std::function &f1, const std::function &f2) {    auto start = std::now();    f1();    auto end = std::now();    std::cout << "cin cost " << std::duration_cast(end - start).count() << "ms" << std::endl;    start = std::now();    f2();    end = std::now();    std::cout << "scanf cost " << std::duration_cast(end - start).count() << "ms" << std::endl; } void write() {   std::ofstream file("./data");   for(int i = 0; i < num; ++i) {     file << rand() <<' ';     if((i + 1) % 20 == 0) {       file << std::endl;     }   } } int main() {   std::sync_with_stdio(false);   write();       time_report([](){   freopen("./data","r",stdin);   int n = 0;   for(int i=0; i> n;   } }, [](){   freopen("./data","r",stdin);   int n = 0;   for(int i = 0; i < num; ++i) {      scanf("%d", &n);   } }); return 0; }

编译,运行后,输出如下:

 

cin cost 178ms
scanf cost 189ms

 

可以看出禁用同步后,二者的性能基本一致。

既然禁用同步后,C++流的性能与C基本一致,那么是否直接禁用呢?答案是依赖于具体的使用场景。

1、同步的C++流是线程安全的,也就说来自不同线程的输出可能会交错,但数据不会产生竞争,而如果禁用同步,则可能出现意想不到的结果。

2、如果禁用了同步功能,输入输出顺序可能会得不到我们想要的结果。

 

#include 
#include 

int  main() {
  std::cout << "a ";
  printf("b ");
  std::cout << "c ";
  return 0;
}

 

上述代码执行后,输出a b c ,符合我们的预期。

如果加上禁用同步代码,如下:

 

#include 
#include 

int  main() {
std::sync_with_stdio(false);
std::cout << "a ";
printf("b ");
std::cout << "c ";

return 0;
}

 

编译、运行之后,结果为a c b,与我们期望的不一致。

结语

如果使用C编程,那么使用C stdio,而如果使用C++编程,则建议使用C++ I/O。如果在某些特殊场景下,需要混合使用,那么强烈建议不要禁用同步,否则会得到意想不到的结果。

好了,今天的文章就到这,我们下期见!  

      审核编辑:彭静
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分