CSV分析器的性能瓶颈
我目前的解析器如下 - 读取~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计算。
在我的机器,你的储备代码需要约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);
}
通过读取文件一次而不是两次,您可能只有一半的时间。
虽然认为矢量是有益的,但它永远不会支配运行时,因为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));
@MiloChen它或多或少是'scanf'的C++等价物,但没有格式字符串。它是类型安全的并从流中读取格式化的输入。您可以查看['istream :: operator >>'](http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/)了解更多详情。 – 2013-04-29 22:51:56
您可以询问文件系统字符串有多大,而不是通过读取它来获取大小。您可以在一次读取中将整个字符串猛击到缓冲区中。 (或者,您可以按照16M块的顺序MMAP文件)。在这两种情况下,使用指针进行缓冲区扫描都会非常快。 – 2013-04-29 23:09:40
谢谢@IraBaxter,你能举一个最简单的例子吗? – mchen 2013-04-29 23:14:42
尝试调用
std::ios::sync_with_stdio(false);
在您的程序开始。这使cin
/cout
和scanf
/printf
(我从来没有尝试过这个,但经常看到推荐,如here)之间的(据说很慢)同步失效。请注意,如果您这样做,则不能在程序中混合使用C++风格和C风格的IO。
(此外,奥拉夫Dietsche是完全正确的大约仅读取文件一次。)
显然,文件IO是一个坏主意,只是映射整个文件到内存中,访问 CSV文件作为一个连续的VM块,这招致只有少数系统调用
如果你要读整个文件,那么MMIO没有任何区别。该文件必须阅读 - 没有办法避免这种情况。 MMIO只会在你的内存和磁盘之间放置一层(所以它甚至可能比普通的文件I/O慢)。 – 2013-04-30 13:46:52
不,没有'没有区别',mmio不是标准缓冲IO,它不会在内部缓冲并复制到用户缓冲区。此外,对于大文件,比如几GB,文件IO可以爆炸内核的文件系统缓存,mmio不会,至少对于Windows而言,当我编写D探针时,我遇到了这个问题,我终于设法通过mmio修复它,整个文件的一小部分放入mm中,当调用者请求访问下一个文件块时,滑动窗口 – xwlan 2013-04-30 15:20:44
你尝试剖析这看到瓶颈在哪里?您还使用了哪种平台和编译器,以及哪些优化设置? – 2013-04-29 22:14:03
尝试不使用std :: vector并使用预分配内存的内存结构(例如数组) –
2013-04-29 22:44:22
@PaulR - 非常感谢。我在MSVC 2010和切换到发布版本比调试快得多 - 我认为你不能在调试版本中使用编译器优化? – mchen 2013-04-29 22:49:19