通过xargs进行显式排序并行化 - 来自xargs --max-procs的不完整结果

时间:2015-08-10 18:48:15

标签: bash sorting parallel-processing xargs deduplication

上下文

我需要使用' sort -u'来优化重复数据删除。我的linux机器有一个旧的实现' sort'命令(即5.97)没有' - 并行'选项。虽然'排序'实现可并行化的算法(例如merge-sort),我需要明确这样的并行化。因此,我通过' xargs'手工制作。命令优于〜2.5X w.r.t.单一排序-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" outputs" 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命令和" - 并行" sort命令的选项。

sortu.sh代码

 #!/bin/bash

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

1 个答案:

答案 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 "$@"
    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"