读取大量ASCII码并以二进制形式写入

时间:2013-02-07 20:44:28

标签: c++ optimization data-processing

我有大约1.5 Gb浮点数的数据文件存储为以空格分隔的ASCII文本,例如1.2334 2.3456 3.4567等等。

在处理这些数字之前,我首先将原始文件翻译成二进制格式。这很有用,因为我可以选择是使用float还是double,减少文件大小(double大约800 MB,float大约400 MB),然后读入处理数据后,适当大小的块。

我编写了以下函数来进行ASCII到二进制转换:

template<typename RealType=float>  
void ascii_to_binary(const std::string& fsrc, const std::string& fdst){    
 RealType value;
 std::fstream src(fsrc.c_str(), std::fstream::in | std::fstream::binary);
 std::fstream dst(fdst.c_str(), std::fstream::out | std::fstream::binary);

 while(src >> value){
  dst.write((char*)&value, sizeof(RealType));
 }
 // RAII closes both files
}

我想加快acii_to_binary,我似乎无法想出任何东西。我尝试以8192字节的块读取文件,然后尝试在另一个子例程中处理缓冲区。这似乎非常复杂,因为缓冲区中的最后几个字符可能是空格(在这种情况下一切都很好),或者是截断的数字(非常糟糕) - 处理可能的截断的逻辑似乎不值得。

你会怎么做才能加快这个功能?我宁愿依赖标准C ++(C ++ 11也没关系),没有额外的依赖,比如boost。

谢谢。

编辑:

@DavidSchwarts:

我尝试按如下方式实施您的建议:

 template<typename RealType=float>  
  void ascii_to_binary(const std::string& fsrc, const std::string& fdst{    
    std::vector<RealType> buffer;
    typedef typename std::vector<RealType>::iterator VectorIterator;
    buffer.reserve(65536);

    std::fstream src(fsrc, std::fstream::in | std::fstream::binary);
    std::fstream dst(fdst, std::fstream::out | std::fstream::binary);

    while(true){
      size_t k = 0;
      while(k<65536 && src >> buffer[k]) k++;     
      dst.write((char*)&buffer[0], buffer.size());
      if(k<65536){
    break;
      }
    }
  }

但它似乎没有写数据!我正在努力......

2 个答案:

答案 0 :(得分:3)

我做了完全相同的事情,除了我的字段由制表符'\t'分隔,我还必须处理每行末尾的非数字注释和散布的标题行与数据。

Here是我的实用程序的文档。

我也有速度问题。以下是我为提高性能所做的事情大约20倍:

  • 使用内存映射文件替换显式文件读取。一次映射两个块。处理完行后处于第二个块时,使用第二个和第三个块重新映射。这样,跨越块边界的线在内存中仍然是连续的。 (假设没有行大于块,你可以增加块大小以保证这一点。)
  • 使用_mm_cmpeq_epi8等SIMD说明搜索行结尾或其他分隔符。在我的例子中,任何包含'='字符的行都是需要不同处理的元数据行。
  • 使用准系统号码解析功能(我使用自定义的解析时间以HH:MM:SS格式,strtodstrtol非常适合抓取普通数字)。这些比istream格式化的提取函数快得多。
  • 使用OS文件编写API而不是标准C ++ API。

如果您梦想300,000线/秒的吞吐量,那么您应该考虑采用类似的方法。

当您不使用C ++标准流时,您的可执行文件也会缩小。我有205KB,包括一个图形界面,只依赖于Windows附带的DLL(不需要MSVCRTxx.dll)。再看一遍,我仍在使用C ++流进行状态报告。

答案 1 :(得分:0)

使用std::vector RealType写入聚合到固定缓冲区中。你的逻辑应该像这样工作:

  1. std::vector<RealType>分配65,536个默认构造的条目。

  2. 在向量中最多读取65,536个条目,替换现有条目。

  3. 写出尽可能多的条目。

  4. 如果您准确阅读了65,536个条目,请转到步骤2.

  5. 停止,你已经完成了。

  6. 这将阻止您对两个不同的文件进行交替读写操作,从而显着减少搜索活动。它还允许您减少write次呼叫,减少复制和缓冲逻辑。