Можно ли распараллелить запись awk в несколько файлов через параллельную GNU? - PullRequest
0 голосов
/ 18 октября 2018

Я запускаю сценарий awk, который хочу распараллелить через GNU параллельно.

Этот сценарий демультиплексирует один входной файл на несколько выходных файлов в зависимости от значения в каждой строке.Код следующий:

#!/usr/bin/awk -f

BEGIN{ FS=OFS="\t" }
{
    # bc is the field that defines to which file the line
    # will be written
    bc = $1
    # append line to such file
    print >> (bc".txt")
}

Я хочу распараллелить его, используя параллельную GNU, с помощью следующего:

parallel --line-buffer --block 1G --pipe 'awk script.awk'

Однако я боюсь возможных состояний гонки, при которых два процесса awkпишут в одном и том же файле одновременно.Возможно ли это, и если да, то как избежать этого, не ставя под угрозу распараллеливание?

NB.Я включил параметр --line-buffer, хотя я не уверен, относится ли он также к перенаправлению файлов в сценарии awk.Это применимо также в этом случае или только к стандартному выводу каждого процесса awk?

Пример

# Input file
bc1    line1
bc3    line2
bc1    line3
bc2    line4


# Output file bc1.txt
bc1    line1
bc1    line3

# Output file bc2.txt
bc2    line4

# Output file bc3.txt
bc3    line2

1 Ответ

0 голосов
/ 19 октября 2018

Вы можете сделать это, демультиплексировав выходные данные в разных каталогах:

stuff |
  parallel --block 10M --pipe --round-robin \
    'mkdir -p dir-{%}; cd dir-{%}; awk ../script.awk'

Или, если вход является файлом, вы можете использовать --pipepart, что быстрее:

parallel --block -1 --pipepart -a bigfile \
  'mkdir -p dir-{%}; cd dir-{%}; awk ../script.awk'

Тогда нет расы.Завершите слияние каталогов:

parallel 'cd {}; ls' ::: dir-* | sort -u |
  parallel 'cat */{} > {}'

Если слияние недопустимо (возможно, у вас нет места на диске для 2 копий данных), вы можете использовать fifos.Но для этого вам нужно заранее знать имена всех .txt -файлов, и вам нужна система, которая может параллельно запускать один процесс для каждого имени (10000 имен = 10000 процессов):

# Generate names-of-files.txt somehow
# Make fifos for all names in all slots
parallel 'mkdir -p {2}; mkfifo {2}/{1}' :::: \
  names-of-files.txt <(seq $(parallel --number-of-threads) )
# Run the demultiplexer in the background
parallel --block -1 --pipepart -a bigfile \
  'mkdir -p dir-{%}; cd dir-{%}; awk ../script.awk' &
# Start one process per name
# If you have more than 32000 names, you will need to increase the number
# of processes on your system.
cat names-of-files.txt |
  parallel -j0 --pipe -N250 -I ,, parallel -j0 'parcat */{} > {}'
...