Мне нужно читать огромный 35G файл с диска построчно в C ++. В настоящее время я делаю это следующим образом:
ifstream infile("myfile.txt");
string line;
while (true) {
if (!getline(infile, line)) break;
long linepos = infile.tellg();
process(line,linepos);
}
Но это дает мне производительность около 2 МБ / с, хотя файловый менеджер копирует файл со скоростью 100 МБ / с. я думаю что getline()
не делает буферизацию правильно. Пожалуйста, предложите какой-нибудь буферизованный метод построчного чтения.
UPD: process () не является узким местом, код без process () работает с той же скоростью.
Вы не приблизитесь к скорости линии со стандартными потоками ввода-вывода. Буферизация или нет, почти ЛЮБОЙ разбор убьет вашу скорость на порядки. Я проводил эксперименты с файлами данных, состоящими из двух целых и двойного числа на строку (чип Ivy Bridge, SSD):
f >> i1 >> i2 >> d
) быстрее, чем getline
в строку, за которой следует sstringstream
разобрать.fscanf
получить около 40 МБ / с.getline
без разбора: 180 МБ / с.fread
: 500-800 МБ / с (в зависимости от того, был ли файл кэширован ОС).Ввод / вывод не является узким местом, разбор есть. Другими словами, ваш process
скорее всего, ваша медленная точка.
Поэтому я написал параллельный парсер. Он состоит из задач (с использованием конвейера TBB):
fread
большие куски (одна такая задача за раз)У меня могут быть неограниченные задачи разбора, потому что мои данные в любом случае неупорядочены. Если нет, то это может не стоить того.
Такой подход дает мне около 100 МБ / с на 4-ядерном чипе IvyBridge.
Я перевел свой собственный код буферизации из моего проекта Java, и он делает то, что мне нужно. Мне пришлось поставить определения, чтобы преодолеть проблемы с Tellg компилятора M $ VC 2010, который всегда дает неправильные отрицательные значения на больших файлах. Этот алгоритм дает желаемую скорость ~ 100 МБ / с, хотя он делает некоторые бесполезные новые [].
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);
}
Используйте анализатор строк или напишите то же самое. вот пример в sourceforge http://tclap.sourceforge.net/ и положить в буфер при необходимости.