Эффективный способ сохранения данных на диск во время выполнения задачи, требующей большого объема вычислений
Я работаю над научным программным обеспечением, которое очень интенсивно использует процессор (его привязка к процессу), но оно должно записывать данные на диск довольно часто (привязано к вводу / выводу).
Я добавляю распараллеливание к этому (OpenMP) и мне интересно, как лучше всего удовлетворить потребности записи на диск. Нет причин, по которым симуляция должна ждать на жестком диске (что сейчас и делается).
Я ищу "лучшую практику" для этого, и скорость - это то, что меня волнует больше всего (это могут быть очень длинные симуляции).
Спасибо ~ Алекс
Первые мысли:
при наличии отдельного процесса выполняется фактическая запись на диск, поэтому моделирование имеет два процесса: один связан с процессором (моделирование), а другой - с вводом-выводом (файл записи). Это звучит сложно.
Возможно труба / буфер? Я новичок в этом, так что, возможно, это может быть возможным решением.
6 ответов
Если вы реализуете OpenMP в своей программе, то лучше использовать #pragma omp single или #pragma omp master из параллельного раздела для сохранения в файл. Эти прагмы позволяют только одному потоку выполнять что-то. Итак, ваш код может выглядеть следующим образом:
#pragma omp parallel
{
// Calculating the first part
Calculate();
// Using barrier to wait all threads
#pragma omp barrier
#pragma omp master
SaveFirstPartOfResults();
// Calculate the second part
Calculate2();
#pragma omp barrier
#pragma omp master
SaveSecondPart();
Calculate3();
// ... and so on
}
Здесь команда потоков выполнит расчет, но только один поток сохранит результаты на диск.
Это похоже на программный конвейер. Я предлагаю вам рассмотреть шаблон tbb::pipeline из библиотеки Intel Threading Building Blocks. Я могу отослать вас к учебнику по программным конвейерам по адресу http://cache-www.intel.com/cd/00/00/30/11/301132_301132.pdf. Пожалуйста, прочитайте пункт 4.2. Они решили проблему: один поток для чтения с диска, второй для обработки строк чтения, третий для сохранения на диск.
Я бы сказал, что лучшим способом было бы создать другой поток для сохранения данных, а не совершенно новый процесс; с новым процессом вы сталкиваетесь с необходимостью передавать данные, подлежащие сохранению, через границы процесса, что создает новый набор трудностей.
Первое, что приходит на ум, - это почти то, что вы сказали, - запись на диск в своем собственном процессе с односторонним каналом от сима к автору. Автор делает запись максимально быстро (получение новых данных с конвейера). Проблема в том, что если сим будет слишком далеко опережать писателя, сим все равно будет блокировать запись в трубу, и он будет привязан к вводу / выводу при одном удалении.
Проблема в том, что на самом деле ваш цикл симуляции не завершен, пока он не выплюнет результаты.
Второе, что приходит мне в голову, - это использовать неблокирующий ввод / вывод. Всякий раз, когда сим должен писать, он должен делать это через неблокирующий ввод / вывод. При следующей необходимости записи он может получить результаты предыдущей операции ввода-вывода (возможно, с небольшим ожиданием) перед запуском новой. Это позволяет симуляции работать максимально параллельно с вводом / выводом, не позволяя симуляции опередить процесс написания.
Первое решение было бы лучше, если цикл обработки симуляции изменяется (иногда меньше, чем время записи, иногда дольше), потому что в среднем записи могут идти в ногу с симом.
Если цикл обработки всегда (или почти всегда) будет короче, чем время записи, то вы также можете не беспокоиться о конвейере и просто использовать неблокирующий ввод / вывод, потому что если вы используете конвейер, он в конечном итоге заполнится и сим все равно будет зависать на вводе / выводе.
Поскольку вы связаны с процессором и IO: позвольте мне угадать: памяти все еще достаточно, верно?
Если это так, вы должны буферизовать данные, которые должны быть записаны на диск в памяти в определенной степени. Написание огромных порций данных обычно выполняется намного быстрее, чем написание небольших фрагментов.
Для самой записи: рассмотрите возможность использования ввода-вывода с отображением памяти. Прошло много времени с тех пор, как я провёл тестирование, но в прошлый раз я сделал это значительно быстрее.
Также вы всегда можете немного обменять процессор против ввода-вывода. Я думаю, что вы в настоящее время пишете данные как некие необработанные, несжатые данные, верно? Вы можете получить некоторую производительность ввода-вывода, если будете использовать простую схему сжатия, чтобы уменьшить объем данных, которые будут записаны. Библиотека ZLIB довольно проста в работе и очень быстро сжимается на самом низком уровне сжатия. Это зависит от характера ваших данных, но если в них много избыточности, то даже очень грубый алгоритм сжатия может устранить проблему, связанную с вводом-выводом.
Сделайте так, чтобы ваше приложение имело два потока, один для процессора и один для жесткого диска.
Пусть поток ЦП помещает завершенные данные в очередь, из которой затем извлекается поток жесткого диска при поступлении данных.
Таким образом, процессор просто избавляется от данных и позволяет кому-то другому обрабатывать их, а жесткий диск терпеливо ждет любых данных в своей очереди.
С точки зрения реализации, вы можете сделать очередь как объект с разделяемой памятью, но я думаю, что канал будет именно тем, что вы ищете. Процессор просто пишет в канал, когда это необходимо. Что касается жесткого диска, вы просто читаете канал и всякий раз, когда получаете действительные данные, продолжайте оттуда.
Один поток постоянно выполняет этап вычислительно интенсивного процесса, а затем добавляет частичный результат в очередь частичных результатов. Другой поток постоянно удаляет частичные результаты из очереди и записывает их на диск. Обязательно синхронизируйте доступ к очереди. Очередь представляет собой структуру данных в виде списка, в которую можно добавлять элементы в конец и удалять элементы с фронта.