У меня есть файлы данных с числами с плавающей точкой около 1,5 Гб, которые хранятся в виде текста ASCII, разделенного пробелами, например, 1.2334 2.3456 3.4567
и так далее.
Перед обработкой таких чисел я сначала перевожу исходный файл в двоичный формат. Это полезно, потому что я могу выбрать, использовать ли float
или же double
уменьшите размер файла (до 800 МБ для double
и 400 МБ для float
) и прочитайте кусками соответствующего размера после обработки данных.
Я написал следующую функцию, чтобы сделать ASCII-двоичный перевод:
template<typename RealType=float>
void ascii_to_binary(const std::string& fsrc, const std::string& fdst){
RealType value;
std::fstream src(fsrc.c_str(), std::fstream::in | std::fstream::binary);
std::fstream dst(fdst.c_str(), std::fstream::out | std::fstream::binary);
while(src >> value){
dst.write((char*)&value, sizeof(RealType));
}
// RAII closes both files
}
Я хотел бы ускорить acii_to_binary
Кажется, я ничего не могу придумать. Я попытался прочитать файл кусками по 8192 байта, а затем попытаться обработать буфер в другой подпрограмме. Это кажется очень сложным, потому что последние несколько символов в буфере могут быть пробелом (в этом случае все хорошо) или усеченным числом (что очень плохо) — логика для обработки возможного усечения вряд ли стоит.
Что бы вы сделали, чтобы ускорить эту функцию? Я предпочел бы полагаться на стандарт C ++ (C ++ 11 в порядке) без дополнительных зависимостей, таких как boost.
Спасибо.
@DavidSchwarts:
Я попытался реализовать ваше предложение следующим образом:
template<typename RealType=float>
void ascii_to_binary(const std::string& fsrc, const std::string& fdst{
std::vector<RealType> buffer;
typedef typename std::vector<RealType>::iterator VectorIterator;
buffer.reserve(65536);
std::fstream src(fsrc, std::fstream::in | std::fstream::binary);
std::fstream dst(fdst, std::fstream::out | std::fstream::binary);
while(true){
size_t k = 0;
while(k<65536 && src >> buffer[k]) k++;
dst.write((char*)&buffer[0], buffer.size());
if(k<65536){
break;
}
}
}
Но, похоже, данные не пишутся! Я работаю над этим…
я сделал именно так то же самое, за исключением того, что мои поля были разделены вкладкой '\t'
и мне пришлось также обрабатывать нечисловые комментарии в конце каждой строки и строки заголовка с вкраплениями данных.
Вот это документация для моей утилиты.
И у меня тоже была проблема со скоростью. Вот что я сделал, чтобы повысить производительность примерно в 20 раз:
_mm_cmpeq_epi8
искать окончания строк или другие символы-разделители. В моем случае любая строка, содержащая '='
символом была строка метаданных, которая требовала другой обработки.strtod
а также strtol
идеально подходят для захвата обычных номеров). Это намного быстрее, чем istream
отформатированные функции извлечения.Если вы мечтаете о пропускной способности в диапазоне 300 000 строк / секунду, то вы должны рассмотреть аналогичный подход.
Ваш исполняемый файл также уменьшается, когда вы не используете стандартные потоки C ++. У меня есть 205 КБ, включая графический интерфейс, и зависит только от библиотек DLL, которые поставляются с Windows (MSVCRTxx.dll не требуется). И еще раз, я все еще использую потоки C ++ для отчетов о состоянии.
Агрегировать пишет в фиксированный буфер, используя std::vector
из RealType
, Ваша логика должна работать так:
Выделить std::vector<RealType>
с 65 536 записями по умолчанию.
Прочитайте до 65 536 записей в векторе, заменив существующие записи.
Запишите столько записей, сколько вы смогли прочитать.
Если вы прочитали ровно 65 536 записей, перейдите к шагу 2.
Стоп, все готово.
Это предотвратит чередование операций чтения и записи в двух разных файлах, что значительно минимизирует активность поиска. Это также позволит вам сделать гораздо меньше write
вызовы, уменьшая копирование и буферизацию логики.