2013-04-29 71 views
0

我目前的解析器如下 - 读取~10MB CSV到STL向量需要〜30秒,这对我的喜好来说太慢了,因为我已经有超过100MB的需要在每次读取程序运行。任何人都可以给一些建议如何提高性能?事实上,在普通C中它会更快吗?CSV分析器的性能瓶颈

int main() { 
    std::vector<double> data; 
    std::ifstream infile("data.csv"); 
    infile >> data; 
    std::cin.get(); 
    return 0; 
} 

std::istream& operator >> (std::istream& ins, std::vector<double>& data) 
{ 
    data.clear(); 

    // Reserve data vector 
    std::string line, field; 
    std::getline(ins, line); 
    std::stringstream ssl(line), ssf; 

    std::size_t rows = 1, cols = 0; 
    while (std::getline(ssl, field, ',')) cols++; 
    while (std::getline(ins, line)) rows++; 

    std::cout << rows << " x " << cols << "\n"; 

    ins.clear(); // clear bad state after eof 
    ins.seekg(0); 

    data.reserve(rows*cols); 

    // Populate data 
    double f = 0.0; 
    while (std::getline(ins, line)) { 
     ssl.str(line); 
     ssl.clear(); 
     while (std::getline(ssl, field, ',')) { 
      ssf.str(field); 
      ssf.clear(); 
      ssf >> f; 
      data.push_back(f); 
     } 
    } 
    return ins; 
} 

注:我也有openMP在我的处置,和内容将最终用于与CUDA的GPGPU计算。

+3

你尝试剖析这看到瓶颈在哪里?您还使用了哪种平台和编译器,以及哪些优化设置? – 2013-04-29 22:14:03

+0

尝试不使用std :: vector 并使用预分配内存的内存结构(例如数组) – 2013-04-29 22:44:22

+0

@PaulR - 非常感谢。我在MSVC 2010和切换到发布版本比调试快得多 - 我认为你不能在调试版本中使用编译器优化? – mchen 2013-04-29 22:49:19

回答

3

在我的机器,你的储备代码需要约1.1秒,你填入代码需要8.5秒。

添加std :: ios :: sync_with_stdio(false);对我的编译器没有任何影响。

下面的C代码需要2.3秒。

int i = 0; 
int j = 0; 
while(true) { 
    float x; 
    j = fscanf(file, "%f", & x); 
    if(j == EOF) break; 
    data[i++] = x; 
    // skip ',' or '\n' 
    int ch = getc(file); 
} 
5

通过读取文件一次而不是两次,您可能只有一半的时间。

虽然认为矢量是有益的,但它永远不会支配运行时,因为I/O将总是比较慢一些。

另一种可能的优化可能是在没有字符串流的情况下进行读取。喜欢的东西(未经测试)

int c = 0; 
while (ins >> f) { 
    data.push_back(f); 
    if (++c < cols) { 
     char comma; 
     ins >> comma; // skip comma 
    } else { 
     c = 0; // end of line, start next line 
    } 
} 

如果你可以省略,和分离仅空格的值,它可能会更

while (ins >> f) 
    data.push_back(f); 

std::copy(std::istream_iterator<double>(ins), std::istream_iterator<double>(), 
      std::back_inserter(data)); 
+1

@MiloChen它或多或少是'scanf'的C++等价物,但没有格式字符串。它是类型安全的并从流中读取格式化的输入。您可以查看['istream :: operator >>'](http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/)了解更多详情。 – 2013-04-29 22:51:56

+0

您可以询问文件系统字符串有多大,而不是通过读取它来获取大小。您可以在一次读取中将整个字符串猛击到缓冲区中。 (或者,您可以按照16M块的顺序MMAP文件)。在这两种情况下,使用指针进行缓冲区扫描都会非常快。 – 2013-04-29 23:09:40

+0

谢谢@IraBaxter,你能举一个最简单的例子吗? – mchen 2013-04-29 23:14:42

2

尝试调用

std::ios::sync_with_stdio(false); 

在您的程序开始。这使cin/coutscanf/printf(我从来没有尝试过这个,但经常看到推荐,如here)之间的(据说很慢)同步失效。请注意,如果您这样做,则不能在程序中混合使用C++风格和C风格的IO。

(此外,奥拉夫Dietsche是完全正确的大约仅读取文件一次。)

-1

显然,文件IO是一个坏主意,只是映射整个文件到内存中,访问 CSV文件作为一个连续的VM块,这招致只有少数系统调用

+0

如果你要读整个文件,那么MMIO没有任何区别。该文件必须阅读 - 没有办法避免这种情况。 MMIO只会在你的内存和磁盘之间放置一层(所以它甚至可能比普通的文件I/O慢)。 – 2013-04-30 13:46:52

+0

不,没有'没有区别',mmio不是标准缓冲IO,它不会在内部缓冲并复制到用户缓冲区。此外,对于大文件,比如几GB,文件IO可以爆炸内核的文件系统缓存,mmio不会,至少对于Windows而言,当我编写D探针时,我遇到了这个问题,我终于设法通过mmio修复它,整个文件的一小部分放入mm中,当调用者请求访问下一个文件块时,滑动窗口 – xwlan 2013-04-30 15:20:44