Прочитайте большое количество чисел ASCII и запишите в двоичном виде

У меня есть файлы данных с числами с плавающей точкой около 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;
}
}
}

Но, похоже, данные не пишутся! Я работаю над этим…

1

Решение

я сделал именно так то же самое, за исключением того, что мои поля были разделены вкладкой '\t' и мне пришлось также обрабатывать нечисловые комментарии в конце каждой строки и строки заголовка с вкраплениями данных.

Вот это документация для моей утилиты.

И у меня тоже была проблема со скоростью. Вот что я сделал, чтобы повысить производительность примерно в 20 раз:

  • Замените явные чтения файла отображенными в память файлами. Карта двух блоков одновременно. Когда вы находитесь во втором блоке после обработки строки, переназначьте второй и третий блоки. Таким образом, линия, которая пересекает границу блока, все еще является непрерывной в памяти. (Предполагается, что ни одна строка не больше блока, возможно, вы можете увеличить размер блока, чтобы гарантировать это.)
  • Используйте инструкции SIMD, такие как _mm_cmpeq_epi8 искать окончания строк или другие символы-разделители. В моем случае любая строка, содержащая '=' символом была строка метаданных, которая требовала другой обработки.
  • Использовать функцию парсинга чисел barebones (я использовал пользовательскую для разбора времени в формате ЧЧ: ММ: СС, strtod а также strtol идеально подходят для захвата обычных номеров). Это намного быстрее, чем istream отформатированные функции извлечения.
  • Используйте API записи файла OS вместо стандартного API C ++.

Если вы мечтаете о пропускной способности в диапазоне 300 000 строк / секунду, то вы должны рассмотреть аналогичный подход.

Ваш исполняемый файл также уменьшается, когда вы не используете стандартные потоки C ++. У меня есть 205 КБ, включая графический интерфейс, и зависит только от библиотек DLL, которые поставляются с Windows (MSVCRTxx.dll не требуется). И еще раз, я все еще использую потоки C ++ для отчетов о состоянии.

3

Другие решения

Агрегировать пишет в фиксированный буфер, используя std::vector из RealType, Ваша логика должна работать так:

  1. Выделить std::vector<RealType> с 65 536 записями по умолчанию.

  2. Прочитайте до 65 536 записей в векторе, заменив существующие записи.

  3. Запишите столько записей, сколько вы смогли прочитать.

  4. Если вы прочитали ровно 65 536 записей, перейдите к шагу 2.

  5. Стоп, все готово.

Это предотвратит чередование операций чтения и записи в двух разных файлах, что значительно минимизирует активность поиска. Это также позволит вам сделать гораздо меньше write вызовы, уменьшая копирование и буферизацию логики.

0

По вопросам рекламы [email protected]