Читать входные файлы, как можно быстрее?
У меня есть множество текстовых файлов данных в виде чисел с плавающей запятой. Я ищу самый быстрый способ прочитать их на С++. Я могу изменить файл на двоичный, если это самый быстрый.
Было бы здорово, если бы вы могли дать мне подсказку или сослаться на сайт с полным объяснением. Я не знаю, есть ли библиотека, которая быстро работает. Даже если есть какое-либо программное обеспечение с открытым исходным кодом, которое выполняет эту работу, это было бы полезно.
Ответы
Ответ 1
Наличие двоичного файла - самый быстрый вариант. Не только вы можете прочитать его непосредственно в массиве с raw istream::read
за одну операцию (что очень быстро), но вы даже можете сопоставить файл в памяти, если ваша ОС поддерживает его; вы можете использовать open
/mmap
для POSIX-систем, CreateFile
/CreateFileMapping
/MapViewOfFile
для Windows или даже для кросс-платформенного решения Boost (спасибо @Cory Nelson за то, что указали его).
Быстрые и грязные примеры, предполагая, что файл содержит необработанное представление некоторого float
s:
"Нормальный":
#include <fstream>
#include <vector>
// ...
// Open the stream
std::ifstream is("input.dat");
// Determine the file length
is.seekg(0, std:ios_base::end);
std::size_t size=is.tellg();
is.seekg(0, std::ios_base::beg);
// Create a vector to store the data
std::vector<float> v(size/sizeof(float));
// Load the data
is.read((char*) &v[0], size);
// Close the file
is.close();
С общей памятью:
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
using boost::interprocess;
// ....
// Create the file mapping
file_mapping fm("input.dat", read_only);
// Map the file in memory
mapped_region region(fm, read_only);
// Get the address where the file has been mapped
float * addr = (float *)region.get_address();
std::size_t elements = region.get_size()/sizeof(float);
Ответ 2
Ваше узкое место находится в I/O. Вы хотите, чтобы программа считывала столько данных в память за наименьшее количество вызовов ввода-вывода. Например, чтение 256 номеров с одним fread
выполняется быстрее 256 fread
одного номера.
Если вы можете, отформатируйте файл данных в соответствии с представлением внутренней плавающей запятой целевой платформы или, по крайней мере, вашим представлением программы. Это уменьшает накладные расходы при переводе текстового представления во внутреннее представление.
Обход ОС и использование контроллера DMA для чтения в файлах данных, если это возможно. Чип DMA берет на себя бремя чтения данных в память с плеч процессора.
Компактный файл данных. Файл данных должен находиться в одном непрерывном наборе секторов на диске. Это уменьшит количество времени, затрачиваемого на различные области на физических пластинах.
Вы запросили эксклюзивный контроль над дисковым ресурсом и процессорами. Заблокируйте все другие несущественные задачи; повысить приоритет выполнения вашей программы.
Используйте несколько буферов, чтобы поддерживать дисковый накопитель. Большая часть времени проводится в ожидании ускорения и замедления жесткого диска. Ваша программа может обрабатывать данные, а что-то еще хранит данные в буфере, что приводит к...
Многопоточность. Создайте один поток для чтения в данных и предупредите задачу обработки, когда буфер не пуст.
Это должно занять некоторое время. Все другие оптимизации приведут к незначительному повышению производительности. (Например, доступ к контроллеру жесткого диска напрямую для передачи в один из ваших буферов.)
Ответ 3
Еще одно внимание к режиму компиляции.
Я попытался разобрать файл с 1М строк. Режим отладки потреблял 50 секунд для анализа данных и добавления в мой контейнер. Режим деблокирования потребляется как минимум в десять раз быстрее, около 4 секунд.
Ниже приведен код для чтения всего файла перед использованием istringstream для анализа данных в виде 2D-точек (,).
vector <float> in_data;
string raw_data;
ifstream ifs;
ifs.open(_file_in.c_str(), ios::binary);
ifs.seekg(0, ios::end);
long length = ifs.tellg();
ifs.seekg(0, ios::beg);
char * buffer;
buffer = new char[length];
ifs.read(buffer, length);
raw_data = buffer;
ifs.close();
delete[]buffer;
cout << "Size: " << raw_data.length()/1024/1024.0 << "Mb" << endl;
istringstream _sstr(raw_data);
string _line;
while (getline(_sstr, _line)){
istringstream _ss(_line);
vector <float> record;
//maybe using boost/Tokenizer is a good idea ...
while (_ss)
{
string s;
if (!getline(_ss, s, ',')) break;
record.push_back(atof(s.c_str()));
}
in_data.push_back(record[0]);
}