【问题标题】:Improve performance of accessing elements in a map and writing to a file提高访问地图元素和写入文件的性能
【发布时间】:2016-01-12 13:31:50
【问题描述】:

我有一个 C++ 中的映射结构,它包含大约 6000 万对。填充地图后,我想将其写入普通文件。

目前这段代码很慢,我想知道您是否对如何加快编写速度有任何建议?

我可以 1) 改进在地图中访问元素的方式吗?或者 2) 通过加速文件写入本身?

map<int,std::pair<int, int>> myMap;
//populate map
typedef map<int,std::pair<int, int>>::const_iterator MapIterator;
int cnt=0;

for (MapIterator iter = myMap.begin(); iter != myMap.end(); ++iter)
{     
    ofile<<iter->second.first<<","<<iter->second.second<<"\n";
    //just printing every 1 million edges written.
    cnt++;
    if(cnt%1000000==0)
        cout<<cnt<<endl;
}

请注意,我必须使用保留键顺序的数据结构(即映射)

【问题讨论】:

  • 什么是“相当慢”?将 6000 万行写入文件可能需要“一段时间”。
  • 您可以通过先写入std::ostringstream 然后将结果以一大块写入磁盘来加快写入速度。
  • 你使用的是什么编译器和操作系统?
  • 要确定“改进空间”的最大界限,您可以写 one 对 6000 万次并测量它。你的代码总是会变慢。
  • 如果你想要快速的东西,存储二进制而不是文本。很有可能您的大部分处理时间都花在了用整数制作字符串。

标签: c++ performance dictionary data-structures


【解决方案1】:

在我的机器上(i7/16gb/win7/vs2013),下面的例子运行在大约:

  • 6s(c 流/二进制);
  • 22s(c 流/文本);
  • 9s(c++ 流/二进制)和
  • 56s(c++ 流/文本)。

在不同的机器上(PentiumD/4gb/win10/vs2015),最长的时间大约是4分钟。

#include <iostream>
#include <cstdint>
#include <map>
#include <chrono>

int main()
{
  typedef std::pair<uint32_t, uint32_t> C_Value;
  typedef std::map< uint32_t, C_Value > C_Map;

  //
  C_Map m;
  const uint32_t maxsize = 50000000;
  try
  {
    for ( int i = 0; i < maxsize; ++i )
      m[i] = C_Value( i, i );
  }
  catch ( ... )
  {
    std::cout << "too many elements\n";
    return -1;
  }

  //
  std::cout << "writing " << m.size() << " elements... ";
  auto t_start = std::chrono::high_resolution_clock::now();

#if 1
  //
  FILE* f = fopen( "test.bin", "wb" );
  if ( ! f )
  {
    std::cout << "could not open file\n";
    return -2;
  }

  //
  for ( auto e : m )
  {
    fwrite( &e.second.first, sizeof e.second.first, 1, f );
    fwrite( &e.second.second, sizeof e.second.second, 1, f );
  }

  //
  fclose( f );
#endif
#if 0
  //
  FILE* f = fopen( "test.bin", "w" );
  if ( ! f )
  {
    std::cout << "could not open file\n";
    return -2;
  }

  //
  for ( auto e : m )
  {
    fprintf( f, "%d, %d\n", e.second.first, e.second.second );
  }

  //
  fclose( f );
#endif
#if 0
  std::ofstream os( "test.bin", std::ios::binary );
  if ( ! os )
  {
    std::cout << "could not open file\n";
    return -2;
  }

  //
  for ( auto e : m )
  {
    os.write( (const char*)&e.second.first, sizeof e.second.first );
    os.write( (const char*)&e.second.second, sizeof e.second.second );
  }

  //
  os.close();
#endif
#if 0
  std::ofstream os( "test.bin" );
  if ( ! os )
  {
    std::cout << "could not open file\n";
    return -2;
  }

  //
  for ( auto e : m )
  {
    os << e.second.first << ',' << e.second.second << '\n';
  }

  //
  os.close();
#endif

  //
  auto t_end = std::chrono::high_resolution_clock::now();
  std::cout << "done in [ms]: " << std::chrono::duration<double, std::milli>( t_end-t_start ).count() << std::endl;

  return 0;
}

【讨论】:

  • 不错。有趣的是,C++ 流比二进制 IO 慢一个数量级,比 C 风格的fprintf() 慢大约三倍。我确实想知道与您已经运行的测试相比,无缓冲简单 open()/write()/close() 做了什么。
  • @Andrew Henle "...慢了一个数量级..."。并不真地。我已经更新了答案以使其清楚。 “我确实想知道什么是无缓冲的......” 它非常慢。我确实尝试过一次(我使用过 Windows 的 API)。
  • @Snps "...using os.sync_with_stdio(false)..." 这对于标准流(如stdin/cin)是有意义的,而不是任意文件流。
  • 谢谢。这非常有用。但是我不能以二进制形式编写文件,因为下一步需要文本格式的文件。但是知道 c 流/文本更快是有用的。
  • @kami 除非你的机器很慢,否则你的时间是可疑的。您应该在您的机器上尝试我的示例,以进行比较。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-04
  • 2023-03-10
  • 2014-01-13
  • 1970-01-01
  • 2012-01-05
  • 1970-01-01
相关资源
最近更新 更多