Почему запись символа без знака* в двоичные файлы с использованием ofstream происходит медленнее, чем ожидалось?

#c #performance #ofstream #file-writing

Вопрос:

Я создал функцию, которая сохраняет данные без знака char* в двоичный файл. Процесс реализуется в отдельном потоке и должен добавлять данные, полученные из очереди (boost::lockfree::очередь). Мой SSD-накопитель теоретически способен развивать скорость записи до ~5000 Мбит/с, однако я заметил, что она вряд ли когда-либо превышает 500 Мбит/с. Я нахожусь на платформе Windows, если это имеет значение. В чем может быть узкое место в приведенном ниже коде?

Настройка: канал камеры —> плата сбора изображений —>> запись данных в память —>>> данные очереди с помощью boost::без блокировки::очередь —>>>> всплывающие данные по требованию —>>>>> сохранить в двоичный файл

 void SaveData::saveAsBinary(std::string filePath, DataFeed *d)
{
    saveThreadADone = false;    // std::atomic<bool> flag

    std::thread storeDataThread([this, filePath, d] {

    std::ofstream file = open(filePath, std::ios::beg | std::ofstream::out | std::ofstream::ate | std::ofstream::binary);
    if(outputFile.fail()){
        throw std::runtime_error("Cannot open file for saving.");
        }
   
        while(!d->isDataEmpty()) // Assume data collection is already done
        {           
            auto bufferData = d->getData().get(); // getData returns std::unique_ptr<unsigned char[]>
            size_t bufferDataSize = sizeof (bufferData);
            file.write(reinterpret_cast<char const*>(bufferData), bufferDataSize);
            if (!file.good())
            {
                throw std::runtime_error("There was an error while saving buffer data to file.");
            }
        }
        file.close();
        this->saveThreadADone = true;
    });

    storeDataThread.join();
}

std::unique_ptr<unsigned char[]> DataFeed::getData()
{
    std::unique_ptr<unsigned char[]> output(nullptr);

    if(!dataQueue.empty()){ // boost::lockfree::queue<unsigned char*> dataQueue {100}
        dataQueue.pop(output);
    }
    return output;
}

bool DataFeed::isDataEmpty()
{
    return dataQueue.empty();
}

// Somewhere else in the code
saveData_->saveAsBinary("specifiedPath", *dataFeed);
 

Комментарии:

1. 1) Какой смысл начинать новый поток, а затем присоединяться к нему? 2) Интересно, насколько велики буферы? Возможно, вы тратите слишком много времени на получение буферов? Это больше похоже на миллион буферов с 5 байтами или на 5 буферов с миллионом байтов?

2. чтобы расширить 1, thread t1{ [](){ ...}}; t1.join(); добавьте накладные расходы на создание и присоединение к потоку, но отсутствие параллелизма в вашем коде

3. Я согласен с @user253751: Запуск и объединение потоков является обширным. Вместо этого я бы подумал о том, чтобы начать поток с самого начала и использовать a std::condition_variable для запуска активации. Таким образом, поток будет спать до тех пор, пока в нем не возникнет необходимости. Я уже делал «сброс файлов в фоновом режиме» таким образом. Я не могу назвать вам фактическую скорость передачи данных. В моем случае это было просто достаточно быстро. Следовательно, я не стал анализировать дальше…

4. Чтобы объяснить дальше: предположим, что поток отсоединен, чтобы он мог выполнять задачу в фоновом режиме. Я могу прочитать информацию о размере буфера с аппаратного обеспечения, и она сообщает, что одно изображение будет занимать 5242880 байт. То, что я сохраняю, — это изображения одно за другим. В зависимости от решения пользователя их может быть либо несколько, либо много.

5. предположим, что нить отсоединена , Почему вы хотите отсоединить нить? Отсоединенная нить больше не может быть соединена, что лишает ее возможности разорвать ее контролируемым образом, когда она больше не нужна. Я не знаю о каком-либо преимуществе в производительности отдельного потока по сравнению с присоединяемым потоком (по крайней мере, пока он просто запущен).