Неловко параллельный рабочий процесс создает слишком много выходных файлов
На кластере Linux бегаю много (N > 10^6
) независимые вычисления. Каждое вычисление занимает всего несколько минут, а результат - несколько строк. когда N
было мало, я мог хранить каждый результат в отдельном файле для последующего анализа. С большим N
Тем не менее, я считаю, что я трачу место для хранения (для создания файла) и простые команды, такие как ls
требуют дополнительного ухода из-за внутренних ограничений bash: -bash: /bin/ls: Argument list too long
,
Каждое вычисление требуется для запуска через qsub
алгоритм планирования, поэтому я не могу создать мастер-программу, которая просто объединяет выходные данные в один файл. Простое решение присоединения к одиночному сбою, когда две программы заканчивают одновременно и чередуют свои результаты. У меня нет доступа администратора к кластеру, поэтому установка общесистемной базы данных не вариант.
Как я могу сопоставить выходные данные из смущающе параллельных вычислений, прежде чем они станут неуправляемыми?
3 ответа
1) Как вы говорите, это не ls
который терпит неудачу; это оболочка, которая делает расширение до начала ls
, Вы можете легко решить эту проблему, используя что-то вроде
find . -type f -name 'GLOB' | xargs UTILITY
например.:
find . -type f -name '*.dat' | xargs ls -l
Вы можете отсортировать вывод, так как find
(для эффективности) не сортирует имена файлов (обычно). Есть много других вариантов find
(например, установка глубины рекурсии каталога, более сложная фильтрация и т. д.) и xargs
(максимальное количество аргументов для каждого вызова, параллельного выполнения и т. д.). Прочитайте man
страницы для деталей.
2) Я не знаю, как вы создаете отдельные файлы, поэтому немного сложно предложить конкретные решения, но вот пара идей:
Если вам нужно создать файлы самостоятельно, и вы можете отложить создание файла до конца задания (скажем, путем буферизации вывода), и файлы будут храниться в файловой системе, которая поддерживает консультативную блокировку или какой-либо другой механизм блокировки, такой как атомарное связывание затем вы можете объединить различные задания в один файл, заблокировав его перед выпуском вывода, а затем разблокировав. Но это много требований. В кластере вы вполне можете сделать это с помощью одного файла для всех заданий, выполняющихся на одном хосте, но с другой стороны, вы можете этого не делать.
Опять же, если вам удастся создать файлы самостоятельно, вы можете атомарно записать каждую строку в общий файл. (Даже NFS поддерживает атомарную запись, но не поддерживает атомарное добавление, см. Ниже.) Вам необходимо добавить уникальный идентификатор задания к каждой строке, чтобы вы могли демультиплексировать его. Тем не менее, это не будет работать, если вы используете какой-то автоматический механизм, такой как "моя работа пишет в
stdout
а затем структура планирования копирует его в файл ", что, к сожалению, является распространенным явлением. (По сути, это предложение очень похоже наMapReduce
стратегия. Может это тебе доступно?)В противном случае, возможно, вы можете просто использовать подкаталоги. Несколько тысяч каталогов по тысяче файлов в каждой гораздо более управляемы, чем один каталог с несколькими миллионами файлов.
Удачи.
Редактировать В соответствии с просьбой, некоторые подробности о 2.2:
Для этого вам нужно использовать функции ввода / вывода Posix, потому что, C
библиотека не обеспечивает атомарной записи. В Posix функция записи всегда пишет атомарно, при условии, что вы укажете O_APPEND
когда вы открываете файл. (На самом деле, это пишет атомарно в любом случае, но если вы не укажете O_APPEND
тогда каждый процесс сохраняет свою позицию в файле, поэтому они перезаписывают друг друга.)
Итак, что вам нужно сделать, это:
В начале программы,
open
файл с опциямиO_WRONLY|O_CREATE|O_APPEND
, (Вопреки тому, что я сказал ранее, это не гарантирует работу на NFS, потому что NFS может не обрабатыватьO_APPEND
должным образом. Более новые версии NFS теоретически могут обрабатывать файлы только для добавления, но они, вероятно, этого не делают. Несколько мыслей об этом чуть позже.) Вы, вероятно, не хотите всегда использовать один и тот же файл, поэтому поместите случайное число где-нибудь в его имени, чтобы у ваших различных заданий были различные варианты.O_CREAT
Афаик, всегда атомарный, даже с дрянными реализациями NFS.Для каждой строки вывода
sprintf
строка к внутреннему буферу, помещая уникальный идентификатор в начале. (Ваша работа должна иметь какой-то уникальный идентификатор; просто используйте это.) [Если вы параноик, начните строку с какого-то рода разделителя записей, а затем число байтов в оставшейся строке - вам придется вставьте это значение после форматирования - чтобы строка выглядела примерно так^0274:xx3A7B29992A04:<274 bytes>\n
, где^
шестнадцатеричный 01 или что-то подобное.]write
вся строка в файл. Проверьте код возврата и количество записанных байтов. Если запись не удалась, попробуйте снова. Если запись была короткой, надеюсь, вы следовали приведенным выше инструкциям "если вы параноик", также попробуйте еще раз.
На самом деле, вы не должны получать короткие записи, но вы никогда не знаете. Написание длины довольно просто; Демультиплексирование немного сложнее, но вы можете пересечь этот мост, когда вам нужно:)
Проблема с использованием NFS немного более раздражающая. Как и в случае с 2.1, самое простое решение - попытаться записать файл локально или использовать некую кластерную файловую систему, которая должным образом поддерживает добавление. (NFSv4 позволяет запрашивать только разрешения на "добавление", а не разрешения на "запись", что может привести к тому, что сервер отклонит запись, если какой-либо другой процесс уже сможет записать смещение, которое вы собираетесь использовать. В этом случае вы Мне нужно искать в конце файла и пробовать запись снова, пока в конце концов это не удастся. Однако у меня сложилось впечатление, что эта функция на самом деле не реализована. Я могу ошибаться.)
Если файловая система не поддерживает добавление, у вас будет другой вариант: выбрать длину строки и всегда записывать это количество байтов. (Очевидно, это проще, если выбранная длина фиксированной строки длиннее самой длинной возможной строки, но возможно записать несколько строк фиксированной длины, если они имеют порядковый номер.) Вам нужно будет гарантировать, что каждое задание записывается в различные смещения, которые можно сделать, разделив номер задания задания на номер файла и номер перемежения, и записав все строки для конкретного задания с его перемежением по модулю числа перемежений, в файл, имя которого включает номер файла. (Это проще всего, если задания нумеруются последовательно.) Можно писать за конец файла, поскольку файловые системы Unix будут - или, по крайней мере, должны - либо вставлять NUL, либо создавать отдельные файлы (которые занимают меньше места, но зависит от размера блока файла).
Другой способ обработки файловых систем, которые не поддерживают добавление, но поддерживают консультативную блокировку диапазона байтов (NFSv4 поддерживает это), заключается в использовании идеи фиксированной длины строки, как указано выше, но с получением блокировки в диапазоне, который должен быть записан ранее. пишу это. Используйте неблокирующую блокировку, и если блокировка не может быть получена, попробуйте снова при следующей кратности смещения строки. Если блокировка может быть получена, прочитайте файл с этим смещением, чтобы убедиться, что в нем нет данных, прежде чем записывать его; затем отпустите замок.
Надеюсь, это поможет.
Если вас интересует только пространство:
parallel --header : --tag computation {foo} {bar} {baz} ::: foo 1 2 ::: bar I II ::: baz . .. | pbzip2 > out.bz2
или короче:
parallel --tag computation ::: 1 2 ::: I II ::: . .. | pbzip2 > out.bz2
GNU Parallel гарантирует, что вывод не будет смешанным.
Если вы заинтересованы в поиске подмножества результатов, посмотрите на --results.
Посмотрите вступительные видео, чтобы узнать больше: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Другая возможность - использовать N файлов, где N больше или равно количеству узлов в кластере, и назначать файлы для вычислений в циклическом порядке. Это должно исключить одновременную запись в любой из файлов, если у вас есть разумная гарантия порядка выполнения ваших вычислений.