Параллельное зависание GNU

У меня есть сценарий bash, который применяет различные преобразования/сопоставления к столбцам файла TSV. Я пытаюсь распараллелить преобразования, используя параллель GNU, однако мой код зависает.

Для простоты рассмотрим cat, преобразователь идентификаторов (т. е. ввод -> вывод) и файл TSV из трех столбцов (генерируемый на лету с использованием paste и seq).

n=1000000
map=cat    # identity: inp -> out

rm -f tmp.col{1,2}.fifo
mkfifo tmp.col{1,2}.fifo
paste <(seq $n) <(seq $n) <(seq $n) \
    | tee >(cut -f1 | $map > tmp.col1.fifo) \
    | tee >(cut -f2 | $map > tmp.col2.fifo) \
    | cut -f3- \
    | paste tmp.col{1,2}.fifo - \
    | python -m tqdm > /dev/null

Приведенный выше код работает нормально.

ПРИМЕЧАНИЕ. python -m tqdm > /dev/null печатает скорость.

Далее мы можем распараллелить задачи сопоставления, используя аргументы --pipe --keep-order GNU Parallel. Вот минимальный параллельный пример, который работает:

seq 100 | parallel --pipe -k -j4 -N10 'cat && sleep 1'

Теперь, сложив все это вместе, вот мой код, который параллельно отображает столбцы TSV:

n=1000000
map=cat   # identity map: inp -> out
rm -f tmp.col{1,2}.fifo
mkfifo tmp.col{1,2}.fifo
paste <(seq $n) <(seq $n) <(seq $n) \
  | tee >(cut -f1 | parallel --id jobA --pipe -k -j4 -N1000 "$map" > tmp.col1.fifo) \
  | tee >(cut -f2 | parallel --id jobB --pipe -k -j4 -N1000 "$map" > tmp.col2.fifo) \
  | cut -f3- \
  | paste tmp.col{1,2}.fifo - \
  | python -m tqdm > /dev/null

Этот код должен был работать, однако этот код зависает. Почему он замерзает и как его разморозить?

Среда: Linux 5.15.0-116-generic, Ubuntu 22.04.4 LTS на x86_64.

Какую фактическую обработку вы выполняете, когда это причудливое расположение имеет смысл?

tripleee 01.08.2024 20:45

Спасибо за вопрос. У меня есть терабайт сжатых данных в формате TSV, которые я использую для обучения моделей машинного обучения. Я хочу выполнить очистку столбцов TSV без создания промежуточных временных файлов. Здесь я выбрал cat для демонстрации, но мои задачи картографии выполняются немного медленно, и я хотел бы их распараллелить (напоминание: никаких временных файлов, так как у нас мало места, и я также предпочитаю избегать дискового ввода-вывода. У меня много ядер ЦП и оперативной памяти, поэтому они не являются узким местом.

Thamme Gowda 01.08.2024 20:50

Я думаю, что относительно простой скрипт Python с использованием multiprocessing был бы намного проще, но смысл зависит от объема данных (в каждом поле и в целом) и типа обработки.

tripleee 01.08.2024 20:53

Согласен, и я уже пробовал многопроцессорность Python. Это сработало, но прирост производительности за счет распараллеливания с Python был близок к производительности FIFO/PIPE вообще без параллелизма. Многопроцессорность Python, похоже, тратит много времени на взаимодействие между процессами. фоновые процессы не могут читать/записывать в STDIN/STDOUT основного процесса (ожидается), мне нужно правильно настроить размеры буфера. Это мой план Б, если я не могу заставить gnu работать параллельно.

Thamme Gowda 01.08.2024 21:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
4
102
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

FIFO имеет ограничения по размеру, можете ли вы реорганизовать скрипт таким образом?

#!/bin/bash

n=${1-10}
map=cat

paste <(paste <(seq $n) <(seq $n)  <(seq $n) | cut -f1 | parallel --id jobA --pipe -k -j4 -N1000 "$map")\
      <(paste <(seq $n) <(seq $n)  <(seq $n) | cut -f2 | parallel --id jobA --pipe -k -j4 -N1000 "$map")\
      <(paste <(seq $n) <(seq $n)  <(seq $n) | cut -f3 ) \
    | python -m tqdm > /dev/null

Бежать с

bash test.sh 100
Ответ принят как подходящий

Это состояние гонки с Fifos, а не с GNU Parallel.

Предположим это:

| tee >(cut -f1 | $map1 > tmp.col1.fifo) \
| tee >(cut -f2 | $map2 > tmp.col2.fifo) \
| cut -f3- \
| paste tmp.col{1,2}.fifo - \

Предположим, что $map1 печатает очень мало, а $map2 печатает много.

paste пытается прочитать строку из tmp.col1.fifo, но читать нечего, поэтому он блокируется. $map2 печатает много в tmp.col2.fifo и заполняет FIFO, поэтому тоже блокируется.

Вам просто повезло, что состояние гонки не поразило вас раньше.

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

Возможно, вы сможете «увеличить» размер FIFO с помощью такого инструмента, как mbuffer:

  | tee >(cut -f1 | parallel --pipe -k -j4 -N1000 "$map" | mbuffer -q -m6M -b5 > tmp.col1.fifo) \
  | tee >(cut -f2 | parallel --pipe -k -j4 -N1000 "$map" | mbuffer -q -m6M -b5 > tmp.col2.fifo) \
  | cut -f3- | mbuffer -q -m6M -b5 \
  | paste tmp.col{1,2}.fifo - \
  | python -m tqdm > /dev/null

Но если вы не уверены, что природа ваших данных не изменится, тогда это хрупкое решение, которое просто отбрасывает банку еще дальше.

Как насчет этого вместо этого?

n=1000000
map=cat   # identity map: inp -> out
rm -f tmp.col{1,2,3,4}.fifo
mkfifo tmp.col{1,2,3,4}.fifo
paste <(seq $n) <(seq $n) <(seq $n) | cut -f1 | parallel --pipe -k -j4 -N1000 "$map" > tmp.col1.fifo &
paste <(seq $n) <(seq $n) <(seq $n) | cut -f2 | parallel --pipe -k -j4 -N1000 "$map" > tmp.col2.fifo &
paste <(seq $n) <(seq $n) <(seq $n) | cut -f3 > tmp.col3.fifo &
paste <(seq $n) <(seq $n) <(seq $n) > tmp.col4.fifo &
paste tmp.col{1,2,3,4}.fifo | python -m tqdm > /dev/null

Вы запустите еще несколько paste, но если процессор не является проблемой, то это не должно привести к гонкам.

(Также: --id (он же --semaphore-name) не используется с --pipe, а только с --semaphore. См. https://www.gnu.org/software/parallel/parallel_options_map.pdf)

(Также: если вам не нужно ровно 1000 записей (-N1000), то --block будет быстрее).

У ОП может быть ограничение сделать paste <(seq $n) <(seq $n) <(seq $n) только один раз.

Philippe 02.08.2024 12:51

Из комментариев: I have a terabyte of compressed data in TSV format так что можно просто распаковать файл несколько раз. Возможно, это пустая трата циклов ЦП, но если циклы ЦП не являются ограничивающим фактором, это может быть достойным решением.

Ole Tange 02.08.2024 13:17

Я не подозревал, что tee — это те, у кого есть взаимоблокировки. Я добавил --id к параллельному мышлению, параллели - это те, у которых есть состояние гонки (поэтому я назначил им идентификаторы различных заданий, надеясь, что это исправит). И @Philippe прав в том, что я предпочитаю читать данные один раз (если это возможно), поскольку мои наборы данных хранятся в сетевой файловой системе, что добавляет дополнительные накладные расходы при многократном чтении. Тем не менее, спасибо за содержательный ответ, @OleTange.

Thamme Gowda 04.08.2024 01:03

@ThammeGowda Можете ли вы увидеть мой удаленный ответ? Если первое paste можно выполнить несколько раз, думаю, ФИФО вам не нужны.

Philippe 04.08.2024 01:25

Я вижу ответ. Это еще один обходной путь, но не очень красивый. С точки зрения дизайна я хотел отделить код устройства чтения от задач устройства отображения, чтобы мы могли изменить код устройства отображения (или, при необходимости, отключить его), не затрагивая код устройства чтения. paste <(seq $n) <(seq $n) <(seq $n) — это просто заполнитель для сложной программы чтения (которая циклически обрабатывает кучу файлов, выполняет некоторые базовые проверки работоспособности, распаковывает и выполняет потоковую передачу на STDOUT).

Thamme Gowda 04.08.2024 01:36

Я только что узнал, что @OleTange — автор параллельной программы GNU! Спасибо за такой замечательный инструмент.

Thamme Gowda 07.08.2024 00:13

Другие вопросы по теме

Похожие вопросы

Чтение поврежденных данных из сокета TCP в Ubuntu Linux
Как настроить докер и запустить докер для golang+redis+postgres и прослушать порт 8089
Как выглядит индексный файл, созданный Linux «ar», в файле .a?
Как одно общее загруженное ядро ​​процессора может повлиять на общую загрузку процессора openmp?
Как одно общее загруженное ядро ​​процессора может повлиять на общую загрузку процессора openmp?
SAP NetWeaver. Невозможно получить дескриптор iamodell.so: libnsl.so.1: невозможно открыть файл общего объекта: такого файла или каталога нет
Я хочу использовать mod_timer для таймера на 10 миллисекунд, но результат всегда 20 миллисекунд
Команда «sed» возвращает «Нет такого файла или каталога» при запуске с помощью доступного сценария?
Ок. что означает символ «@» перед регулярным выражением
Почему адреса файлов, хранящихся на диске (vbox vm), различаются каждый раз, когда я просматриваю их?