2015-08-10 1 views
1

КонтекстаЯвных родами распараллеливание с помощью xargs - Неполные результатов от xargs --max-Procs

мне нужно оптимизировать дедупликации с помощью «своего рода -u» и мой линукс машины имеет старую реализацию команды «сортировка» (т.е. 5.97), который не имеет «параллельного» варианта. Хотя «sort» реализует параллелизуемые алгоритмы (например, merge-sort), мне нужно сделать такую ​​распараллеливание явной. Поэтому я делаю это вручную с помощью команды «xargs», которая превосходит ~ 2.5X w.r.t. к единственному методу «sort -u» ... когда он работает нормально.

Здесь интуиция того, что я делаю.

Я запускаю сценарий bash, который разбивает входной файл (например, file.txt) на несколько частей (например, file.txt.part1, file.txt.part2, file.txt.part3, file.txt.part4) , Полученные части передаются команде «xargs» для выполнения параллельной дедупликации через скрипт sortu.sh (подробности в конце). sortu.sh завершает вызов 'sort -u' и выводит результирующее имя файла (например, «sortu.sh file.txt.part1» выводит «file.txt.part1.sorted»). Затем полученные отсортированные части передаются в «sort -merge -u», который объединяет/дедуплицирует входные части, предполагая, что такие части уже отсортированы.

Проблема, с которой я столкнулась, заключается в параллелизации с помощью «xargs». Вот упрощенная версия моего кода:

AVAILABLE_CORES=4 
PARTS="file.txt.part1 
file.txt.part2 
file.txt.part3 
file.txt.part4" 

SORTED_PARTS=$(echo "$PARTS" | xargs --max-args=1 \ 
             --max-procs=$AVAILABLE_CORES \ 
             bash sortu.sh \ 
       ) 
... 
#More code for merging the resulting parts $SORTED_PARTS 
... 

Предполагаемый результат список отсортированных частей в переменной SORTED_PARTS:

echo "$SORTED_PARTS" 
file.txt.part1.sorted 
file.txt.part2.sorted 
file.txt.part3.sorted 
file.txt.part4.sorted 

Симптом

Тем не менее, (иногда) есть отсутствующая отсортированная часть. Например, file.txt.part2.sorted:

echo "$SORTED_PARTS" 
file.txt.part1.sorted 
file.txt.part3.sorted 
file.txt.part4.sorted 

Этот симптом не является детерминированным в его возникновения (т.е. выполнение для того же file.txt успешно и в другое время она не) или пропавших без вести файл (т. е. это не всегда одна и та же отсортированная недостающая часть).

Проблема

У меня есть race condition, где все экземпляры sortu.sh закончить и «xargs» посылает EOF, прежде чем стандартный вывод промывается.

Вопрос

Есть ли способ, чтобы обеспечить стандартный вывод промывку до того 'xagrs' посылает EOF?

Ограничения

Я не в состоянии использовать ни parallel команды, ни "--parallel" вариант sort команды.

sortu.sh код

#!/bin/bash 

SORTED=$1.sorted 
sort -u $1 > $SORTED 
echo $SORTED 
+0

Есть ли ошибки на stderr, если это произойдет? –

+1

Я думаю, что вы видите условие гонки, связанное с тем, что, хотя замена команды может быть завершена, как только «xargs» завершается, сам «xargs» производит * no * output; только его дети пишут в файл, унаследованный от 'xargs'. Поскольку этот вывод буферизуется, есть шанс, что оболочка читает из этого файла, прежде чем вывод из всех дочерних элементов будет сброшен в файл. – chepner

+0

Что такое многострочные строки для списков имен файлов вместо правильных массивов? –

ответ

1

Ниже не записывает содержимое на диск на всех, и параллелизует процесс разделения, процессы сортировки и слияния, выполняя все эти сразу ,

Эта версия была передана в bash 3.2; версия, построенная для новых версий bash, не нужна eval.

#!/bin/bash 

nprocs=5 # maybe call nprocs command instead? 
fd_min=10 # on bash 4.1, can use automatic FD allocation instead 

# create a temporary directory; delete on exit 
tempdir=$(mktemp -d "${TMPDIR:-/tmp}/psort.XXXXXX") 
trap 'rm -rf "$tempdir"' 0 

# close extra FDs and clear traps, before optionally executing another tool. 
# 
# Doing this in subshells ensures that only the main process holds write handles on the 
# individual sorts, so that they exit when those handles are closed. 
cloexec() { 
    local fifo_fd 
    for ((fifo_fd=fd_min; fifo_fd < (fd_min+nprocs); fifo_fd++)); do 
     : "Closing fd $fifo_fd" 
     # in modern bash; just: exec {fifo_fd}>&- 
     eval "exec ${fifo_fd}>&-" 
    done 
    if (($#)); then 
     trap - 0 
     exec "[email protected]" 
    fi 
} 

# For each parallel process: 
# - Run a sort -u invocation reading from an FD and writing from a FIFO 
# - Add the FIFO's name to a merge sort command 
merge_cmd=(sort --merge -u) 
for ((i=0; i<nprocs; i++)); do 
    mkfifo "$tempdir/fifo.$i"    # create FIFO 
    merge_cmd+=("$tempdir/fifo.$i")  # add to sort command line 
    fifo_fd=$((fd_min+i)) 
    : "Opening FD $fifo_fd for sort to $tempdir/fifo.$i" 
    # in modern bash: exec {fifo_fd}> >(cloexec sort -u >$fifo_fd) 
    printf -v exec_str 'exec %q> >(cloexec; exec sort -u >%q)' "$fifo_fd" "$tempdir/fifo.$i" 
    eval "$exec_str" 
done 

# Run the big merge sort recombining output from all the FIFOs 
cloexec "${merge_cmd[@]}" & 
merge_pid=$! 

# Split input stream out to all the individual sort processes... 
awk -v "nprocs=$nprocs" \ 
    -v "fd_min=$fd_min" \ 
    '{ print $0 >("/dev/fd/" (fd_min + (NR % nprocs))) }' 

# ...when done, close handles on the FIFOs, so their sort invocations exit 
cloexec 

# ...and wait for the merge sort to exit 
wait "$merge_pid" 
+0

Спасибо @CharlesDuffy за ваш сложный ответ. Я проверю его и сравню с моим. – Manolo

+0

Единственное место, где это может быть медленнее, - это то, что вход уже отсортирован. В этом случае требуется более сложный алгоритм разбиения, но поскольку многие из этих более сложных подходов требуют знания общего количества строк вверх, они добавляют затраты на ввод-вывод. Таким образом, крошечный маленький скрипт awk делает простую, глупую вещь. Разделение на большие партии было бы более простым способом уменьшить эту стоимость без IO: просто сделайте '(NR% nprocs)' вместо be '(int (NR/100)% nprocs)', скорректировав '100', как это необходимо для размер партии. –

+0

... использование слишком большого размера партии означает, что ваши другие экземпляры 'sort' сидят, ожидая, когда сплиттер даст им вход, поэтому вы не хотите слишком заходить слишком далеко в этом направлении; хотел бы проверить ваши фактические данные, чтобы узнать, что оптимально. –