GNU Parallel:将文件拆分为子文件

fir*_*ter 6 parallel-processing bash gnu-parallel

目标

使用GNU Parallel将大的.gz文件拆分为子文件.由于服务器有16个CPU,因此创建16个子节点.每个孩子最多应包含N行.这里,N = 104,214,420行.儿童应该是.gz格式.

输入文件

  • name:file1.fastq.gz
  • 大小:39 GB
  • 行数:1,667,430,708(未压缩)

硬件

  • 36 GB内存
  • 16个CPU
  • HPCC环境(我不是管理员)

版本1

zcat "${input_file}" | parallel --pipe -N 104214420 --joblog split_log.txt --resume-failed "gzip > ${input_file}_child_{#}.gz"
Run Code Online (Sandbox Code Playgroud)

三天后,工作还没完成.split_log.txt为空.输出目录中没有可见的子项.日志文件表明Parallel --block-size已从1 MB(默认值)增加到2 GB以上.这激发了我将代码更改为版本2.

版本2

# --block-size 3000000000 means a single record could be 3 GB long. Parallel will increase this value if needed.

zcat "${input_file}" | "${parallel}" --pipe -N 104214420 --block-size 3000000000 --joblog split_log.txt --resume-failed "gzip > ${input_file}_child_{#}.gz"
Run Code Online (Sandbox Code Playgroud)

这项工作已经运行了大约2个小时.split_log.txt为空.尚未在输出目录中看到子项.到目前为止,日志文件显示以下警告:

parallel: Warning: --blocksize >= 2G causes problems. Using 2G-1.
Run Code Online (Sandbox Code Playgroud)

问题

  1. 我的代码如何改进?
  2. 有没有更快的方法来实现这一目标?

Ole*_*nge 4

我们假设该文件是一个 fastq 文件,因此记录大小为 4 行。

您将其告诉 GNU Parallel -L 4

在 fastq 文件中,顺序并不重要,因此您希望将 n*4 行块传递给子级。

为了有效地做到这一点,您使用--pipe-part, except--pipe-part不适用于压缩文件,并且不适用于-L,因此您必须适应--pipe

zcat file1.fastq.gz |
  parallel -j16 --pipe -L 4 --joblog split_log.txt --resume-failed "gzip > ${input_file}_child_{#}.gz"
Run Code Online (Sandbox Code Playgroud)

这会将一个块传递给 16 个子级,一个块默认为 1 MB,在记录边界(即 4 行)处被截断。它将为每个块运行一个作业。但您真正想要的是将输入总共仅传递给 16 个作业,并且您可以进行循环。不幸的是 中存在随机性元素--round-robin,因此--resume-failed不起作用:

zcat file1.fastq.gz |
  parallel -j16 --pipe -L 4 --joblog split_log.txt --round-robin "gzip > ${input_file}_child_{#}.gz"
Run Code Online (Sandbox Code Playgroud)

parallel将很难跟上 16 gzip,但您应该能够压缩 100-200 MB/s。

现在,如果你有未压缩的 fastq 文件,我们可以做得更快,但我们必须作一点欺骗:通常在 fastq 文件中,你会有一个以相同字符串开头的 seqname:

@EAS54_6_R1_2_1_413_324
CCCTTCTTGTCTTCAGCGTTTCTCC
+
;;3;;;;;;;;;;;;7;;;;;;;88
@EAS54_6_R1_2_1_540_792
TTGGCAGGCCAAGGCCGATGGATCA
+
;;;;;;;;;;;7;;;;;-;;;3;83
@EAS54_6_R1_2_1_443_348
GTTGCTTCTGGCGTGGGTGGGGGGG
+EAS54_6_R1_2_1_443_348
;;;;;;;;;;;9;7;;.7;393333
Run Code Online (Sandbox Code Playgroud)

这里是@EAS54_6_R。不幸的是,这也是质量线中的有效字符串(这是一个非常愚蠢的设计),但在实践中,我们会非常惊讶地看到以 开头的质量线@EAS54_6_R。它只是不会发生。

我们可以利用这一点来发挥我们的优势,因为现在您可以使用\n后跟@EAS54_6_R作为记录分隔符,然后我们可以使用--pipe-part. 额外的好处是顺序将保持不变。在这里,您必须将块大小指定为 大小的 1/16 file1-fastq

parallel -a file1.fastq --block <<1/16th of the size of file1.fastq>> -j16 --pipe-part --recend '\n' --recstart '@EAS54_6_R' --joblog split_log.txt "gzip > ${input_file}_child_{#}.gz"
Run Code Online (Sandbox Code Playgroud)

如果您使用 GNU Parallel 20161222,那么 GNU Parallel 可以为您完成该计算。--block -1意思是:选择一个块大小,以便可以为 16 个作业槽中的每一个分配一个块。

parallel -a file1.fastq --block -1 -j16 --pipe-part --recend '\n' --recstart '@EAS54_6_R' --joblog split_log.txt "gzip > ${input_file}_child_{#}.gz"
Run Code Online (Sandbox Code Playgroud)

这里 GNU Parallel 不会成为限制因素:它可以轻松传输 20 GB/s。

必须打开文件才能查看重新启动值应该是什么,这很烦人,因此这在大多数情况下都有效:

parallel -a file1.fastq --pipe-part --block -1 -j16 
--regexp --recend '\n' --recstart '@.*\n[A-Za-z\n\.~]'
my_command
Run Code Online (Sandbox Code Playgroud)

这里我们假设这些行将像这样开始:

@<anything>
[A-Za-z\n\.~]<anything>
<anything>
<anything>
Run Code Online (Sandbox Code Playgroud)

即使你有一些以“@”开头的质量行,那么它们后面也永远不会跟着以 [A-Za-z\n.~] 开头的行,因为质量行后面总是跟着 seqname 行,这以。。开始 @。


您还可以使用一个很大的块大小,使其相当于未压缩文件的 1/16,但这将是一个坏主意:

  • 您必须能够将完整的未压缩文件保存在 RAM 中。
  • 最后一个gzip只会在读取最后一个字节后启动(第一个字节gzip可能会在那时完成)。

通过将记录数设置为 104214420(使用 -N),这基本上就是您正在做的事情,并且您的服务器可能正在努力将 150 GB 未压缩数据保留在其 36 GB RAM 中。