производительность — Улучшение / оптимизация скорости записи файлов в Stack Overflow

Я сталкивался с некоторыми проблемами при записи в файл, а именно с невозможностью писать достаточно быстро.

Чтобы объяснить, моя цель состоит в том, чтобы захватить поток данных, поступающих через гигабитный Ethernet, и просто сохранить его в файл.

Необработанные данные поступают со скоростью 10 мс / с, а затем сохраняются в буфер и затем записываются в файл.

Ниже приведен соответствующий раздел кода:

    std::string path = "Stream/raw.dat";
ofstream outFile(path, ios::out | ios::app| ios::binary);

if(outFile.is_open())
cout << "Yes" << endl;

while(1)
{
rxSamples = rxStream->recv(&rxBuffer[0], rxBuffer.size(), metaData);
switch(metaData.error_code)
{

//Irrelevant error checking...

//Write data to a file
std::copy(begin(rxBuffer), end(rxBuffer), std::ostream_iterator<complex<float>>(outFile));
}
}

Проблема, с которой я сталкиваюсь, заключается в том, что запись файлов в файл занимает слишком много времени. Примерно через секунду устройство, отправляющее сэмплы, сообщает о переполнении буфера. После некоторого быстрого профилирования кода почти все время выполнения тратится на std::copy(...) (99,96% времени, чтобы быть точным). Если я удалю эту строку, я смогу запустить программу в течение нескольких часов, не обнаруживая переполнения.

Тем не менее, я довольно озадачен тем, как я могу улучшить скорость записи. Я просмотрел несколько постов на этом сайте, и кажется, что наиболее распространенным предложением (в отношении скорости) является осуществление записи в файл, как я уже сделал — с помощью std::copy,

Если это полезно, я запускаю эту программу на Ubuntu x86_64. Мы ценим любые предложения.

10

Решение

Таким образом, основная проблема заключается в том, что вы пытаетесь писать в том же потоке, что и вы, что означает, что ваш recv () может быть вызван снова только после завершения копирования. Несколько замечаний:

  • Переместите запись в другой поток. Речь идет о USRP, поэтому GNU Radio действительно может быть инструментом по вашему выбору — он по своей сути многопоточный.
  • Ваш выходной итератор, вероятно, не самое эффективное решение. Просто «write ()» в дескриптор файла может быть лучше, но это измерения производительности, которые вам решать
  • Если ваш жесткий диск / файловая система / ОС / ЦП не соответствуют скоростям, поступающим от USRP, даже если вы отделяете прием от записи по потокам, тогда вы ничего не можете сделать — получить более быструю систему.
  • Попробуйте вместо этого записать на диск RAM

На самом деле, я не знаю, как вы придумали std::copy подход. Пример rx_samples_to_file, который поставляется с UHD делает это с простой записью, и вы должны определенно одобрить это по сравнению с копированием; Файловый ввод / вывод в хороших ОС часто выполняется с одной копией меньше, и перебор всех элементов, вероятно, очень медленный.

13

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

Давайте сделаем немного математики.

Ваши образцы (по-видимому) типа std::complex<std::float>, Учитывая (типичное) 32-битное значение с плавающей запятой, это означает, что каждая выборка имеет размер 64 бита. При скорости 10 мс / с это означает, что необработанные данные составляют около 80 мегабайт в секунду — это то, что вы можете ожидать от записи на жесткий диск настольного компьютера (7200 об / мин), но при этом достаточно близко к пределу (который обычно составляет около 100). -100 мегабайт в секунду или около того).

К сожалению, несмотря на std::ios::binaryвы на самом деле пишете данные в текстовом формате (потому что std::ostream_iterator в основном делает stream << data;).

Это не только теряет некоторую точность, но и увеличивает размер данных, по крайней мере, как правило. Точная величина увеличения зависит от данных — небольшое целочисленное значение может фактически уменьшить количество данных, но для произвольного ввода увеличение размера, близкое к 2: 1, является довольно распространенным явлением. С увеличением 2: 1 ваши исходящие данные теперь составляют около 160 мегабайт в секунду — это быстрее, чем может выдержать большинство жестких дисков.

Очевидной отправной точкой для улучшения будет запись данных в двоичном формате:

uint32_t nItems = std::end(rxBuffer)-std::begin(rxBuffer);
outFile.write((char *)&nItems, sizeof(nItems));
outFile.write((char *)&rxBuffer[0], sizeof(rxBuffer));

На данный момент я использовал sizeof(rxBuffer) в предположении, что это реальный массив. Если это на самом деле указатель или вектор, вам придется вычислить правильный размер (то, что вы хотите, это общее количество записываемых байтов).

Я также хотел бы отметить, что в нынешнем виде ваш код имеет еще более серьезную проблему: поскольку он не указал разделитель между элементами при записи данных, данные будут записаны без чего-либо, чтобы отделить один элемент от следующий. Это означает, что если вы написали два значения (например) 1 а также 0.2то, что вы прочитали бы обратно, не будет 1 а также 0.2, но единственное значение 10.2, Добавление разделителей к вашему текстовому выводу добавит еще больше накладных расходов (примерно на 15% больше данных) к процессу, который уже дает сбой, потому что он генерирует слишком много данных.

Запись в двоичном формате означает, что каждое число с плавающей запятой будет занимать ровно 4 байта, поэтому разделители не нужны для правильного считывания данных обратно.

Следующим шагом после этого будет переход к низкоуровневой процедуре файлового ввода-вывода. В зависимости от ситуации, это может иметь или не иметь большого значения. В Windows вы можете указать FILE_FLAG_NO_BUFFERING когда вы открываете файл с CreateFile, Это означает, что чтение и запись в этот файл будут в основном обходить кеш и переходить непосредственно на диск.

В вашем случае это, вероятно, победа — при 10 мс / с вы, вероятно, собираетесь использовать пространство кеша довольно долго, прежде чем перечитывать те же данные. В таком случае, если вы поместите данные в кеш, вы практически ничего не получите, но вам придется потратить некоторые данные на то, чтобы скопировать данные в кеш, а затем несколько позже скопировать их на диск. Хуже того, он может загрязнить кеш всеми этими данными, поэтому он больше не хранит другие данные, которые с большей вероятностью выиграют от кеширования.

4

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