我需要在C ++中逐行读取大盘中的35G文件。目前我通过以下方式实现:
ifstream infile("myfile.txt");
string line;
while (true) {
if (!getline(infile, line)) break;
long linepos = infile.tellg();
process(line,linepos);
}
但是它给了我大约2MB /秒的性能,尽管文件管理器以100Mb / s的速度复制文件。我想getline()
没有正确缓冲。请提出一些缓冲的逐行读取方法。
UPD:process()不是瓶颈,没有process()的代码以相同的速度工作。
答案 0 :(得分:16)
使用标准IO流,您将无法接近线速度。是否缓冲,几乎任何解析都会使你的速度降低几个数量级。我对由两个整数和一个双线(Ivy Bridge芯片,SSD)组成的数据文件进行了实验:
f >> i1 >> i2 >> d
)比getline
更快地进入字符串,后跟sstringstream
解析。fscanf
这样的C文件操作大约可以达到40 MB / s。getline
没有解析:180 MB / s。fread
:500-800 MB / s(取决于文件是否由操作系统缓存)。 I / O不是瓶颈,解析是。换句话说,您的process
可能是您的慢点。
所以我写了一个并行解析器。它由任务组成(使用TBB管道):
fread
大块(一次一个这样的任务)我可以拥有无限的解析任务,因为无论如何我的数据都是无序的。如果你不是,那么这对你来说可能是不值得的。 这种方法让我在4核IvyBridge芯片上大约100 MB / s。
答案 1 :(得分:3)
我已经从我的java项目中翻译了我自己的缓冲代码,它可以满足我的需求。我不得不放置定义来克服M $ VC 2010编译器tellg的问题,它总是在大文件上给出错误的负值。这个算法给出了所需的速度~100MB / s,虽然它有一些无用的新[]。
void readFileFast(ifstream &file, void(*lineHandler)(char*str, int length, __int64 absPos)){
int BUF_SIZE = 40000;
file.seekg(0,ios::end);
ifstream::pos_type p = file.tellg();
#ifdef WIN32
__int64 fileSize = *(__int64*)(((char*)&p) +8);
#else
__int64 fileSize = p;
#endif
file.seekg(0,ios::beg);
BUF_SIZE = min(BUF_SIZE, fileSize);
char* buf = new char[BUF_SIZE];
int bufLength = BUF_SIZE;
file.read(buf, bufLength);
int strEnd = -1;
int strStart;
__int64 bufPosInFile = 0;
while (bufLength > 0) {
int i = strEnd + 1;
strStart = strEnd;
strEnd = -1;
for (; i < bufLength && i + bufPosInFile < fileSize; i++) {
if (buf[i] == '\n') {
strEnd = i;
break;
}
}
if (strEnd == -1) { // scroll buffer
if (strStart == -1) {
lineHandler(buf + strStart + 1, bufLength, bufPosInFile + strStart + 1);
bufPosInFile += bufLength;
bufLength = min(bufLength, fileSize - bufPosInFile);
delete[]buf;
buf = new char[bufLength];
file.read(buf, bufLength);
} else {
int movedLength = bufLength - strStart - 1;
memmove(buf,buf+strStart+1,movedLength);
bufPosInFile += strStart + 1;
int readSize = min(bufLength - movedLength, fileSize - bufPosInFile - movedLength);
if (readSize != 0)
file.read(buf + movedLength, readSize);
if (movedLength + readSize < bufLength) {
char *tmpbuf = new char[movedLength + readSize];
memmove(tmpbuf,buf,movedLength+readSize);
delete[]buf;
buf = tmpbuf;
bufLength = movedLength + readSize;
}
strEnd = -1;
}
} else {
lineHandler(buf+ strStart + 1, strEnd - strStart, bufPosInFile + strStart + 1);
}
}
lineHandler(0, 0, 0);//eof
}
void lineHandler(char*buf, int l, __int64 pos){
if(buf==0) return;
string s = string(buf, l);
printf(s.c_str());
}
void loadFile(){
ifstream infile("file");
readFileFast(infile,lineHandler);
}
答案 2 :(得分:0)
使用行解析器或编写相同的解析器。这是sourceforge http://tclap.sourceforge.net/中的一个示例,并在必要时放入缓冲区。