【问题标题】:Grep with multi-threading多线程 Grep
【发布时间】:2019-04-12 19:46:18
【问题描述】:

我有以下(大)文件,其中包含 30233088 个字符串:

head mystringfile.txt:

GAATGAACACGAAGAA
GAATGAACACGAAGAC
GAATGAACACGAAGAG
GAATGAACACGAAGCA

cat sequence.txt

AAATAGAGGGCGGTCCAGGCGTGTCGAAACACTGGGTCCAGGGCAAGAGCGGTTCGGGTGTCAGGAAAGCCCCCAAGGGGGTTCGCGCGGTTTGCAGTGAGGTAGAGGCCGGTGTATGGGTAGACAATTGGGGTCCCAAAGAAAAAGGCTCGTCCAACATCATAATAAACCCAAGCACGATAAAAAGCAAACGCAGACTTCAATAGGGTACGAGCAATTGTGGCAGGGTGCTCGCTGTCAGGGTTAGATCTTCTTGGAGTCGCGTCGCTCGGGGGGGCAAGGCCAACGTAAGATCGTGGCTGATCGCTGGCAATGCGGTCGGTTGGGTGGTCGCTAGTAGGGGCACGGCGGTCTCTTATGGCGTCGTAAAATGCGTCTCCAAAGCGAAAAGGGGCGGCAGACAAGTCACCGGGCAAGCTTAGAGGTCTGGGGCCCGTGGCTTTAGGGGAATGAACACGAAGACGCGAAACGAAGTCGTGTTTCTTGTTGGCTGTAGAGGGGAAAACCGTCTGGGGCGATCTGGCGTAGTAGTGCGTGTCTTGCAGTGAGCTCCCCGTCCGTAAGGATTCGCAGGAATCCTGCGTGAAGCTCGGTCGTCTCGGCCGTGTCTCGGGGTTTGATTGCGGGTTCAGATTGGAAAGGTCTCCTCGGGTCGTTTGCTGCATTTGCTCGCAACCCTGACGTGAAAGGGGTGAGCTGTCTCCAATCTGCCACGCTGGGTGTTGCGTCGTCAGTAAAAGACTTGGTCAAGCTGGGACCTCGCAAGATCGCGAGAGGGTTAAGCACAAAAGGTATGGCGAAGCTCCCGGGTGCTCTTGTGGCCACCCAGAATCATGGTGACGTAGGTTTTGCGAAGCCATCAAAAATTCAGGCGGCAAAACGAGCCAGTAGGGTCCTGGGCAGCTGGGCTTGTAGTGGGTAGGCGGCAAAACGCAAAGAATGAACACGAAGCAACTCCGTAGTGTGACGGGGGTTCTGACAAACGTCCTGCAAGAAGTTCGTCTTGGG

我需要在另一个序列文件中grep来确定匹配的位置,我这样做如下:

while read line; do grep -b -o $line sequence.txt >>sequence.txt.count; done<mystringfile.txt

像这样运行代码当然需要很长时间,并且只运行 1 个线程的一部分,所以我该如何修改它(使用 parallelxargs?)以便它在尽可能多的线程上运行我要指定?

【问题讨论】:

  • 你能分享一些那个另一个序列文件用于测试吗?
  • 请考虑我更新的问题

标签: awk parallel-processing grep xargs


【解决方案1】:

你的想法是错误的使用 shell 循环来处理文本。您正在为输入文件上的 30233088 次迭代中的每一次打开一个新的文件描述符,以便重定向到输出文件。它容易产生巨大的性能影响或打开文件描述符用完的情况。

为工作使用正确的工具。 Awk 是你的朋友。如果sequence.txt 只是你所说的一个巨大的模式,你可以将它放入一个变量中以进行正则表达式匹配,如下所示。该解决方案不涉及必须在 RAM 中存储条目的内存开销

awk -v sequence="$(<sequence.txt)" 'n=index(sequence, $1){print n":"$1}' mystringfile.txt

这应该比您使用的方法相对更快,并且要进一步加快速度,请更改您的 locale 设置以匹配 C 本地,

LC_ALL=C awk -v sequence="$(<sequence.txt)" 'n=index(sequence, $1){print n":"$1}' mystringfile.txt

要与grep-b 选项匹配以打印字节偏移开始,请在上面的答案中使用n-1 而不仅仅是n

如果您仍想使用 GNU 并行,请使用 --pipepart 将文件物理拆分为多个部分,并将 --block 的大小指定为要读取多少 MB 的文件内容

parallel -a mystringfile.txt --pipepart --block=20M -q awk -v sequence="$(<sequence.txt)" 'n=index(sequence, $1){print n":"$1}'

【讨论】:

  • 我添加了-P 标志,很好的解决方案!
  • @rororo: -P 标志?您使用了哪种解决方案,效果如何?
  • 我使用了parallel 选项,它在我通过-P 指定的线程数上运行
  • @rororo:所以你用我的答案或者你的-P,你指定了多少线程?花了多少时间?您可以通过将time 放在前面来运行命令并将结果添加到问题中
  • @rororo 您可以通过以下方式修复该错误:$ awk 'NR==FNR{sequence=$1;next}n=index(sequence, $1){print n":"$1}' sequence mystrings
【解决方案2】:

如果您在 mystringfile.txt 中的所有搜索字符串长度相同(例如在您的示例文件中,16 字节),您可以存储所有 16 字节字符串sequence.txt 到一个关联数组(如果你有内存的话)并加快搜索速度。让我们试试看。首先我们需要一些测试材料,让我们创建一个 2400000 字节的sequence.txt,大约需要一秒钟:

$ awk -v seed=$RANDOM 'BEGIN{a[0]="A";a[1]="C";a[2]="G";a[3]="T";srand(seed);for(i=1;i<=2400000;i++)printf "%s", a[int(4*rand())];print ""}' > mystringfile.txt

mystringfile.txt 30233088 16 字节搜索字符串(4 分 50 秒):

$ awk -v seed=$RANDOM 'BEGIN{a[0]="A";a[1]="C";a[2]="G";a[3]="T";srand(seed);for(i=1;i<=30233088;i++){for(j=1;j<=16;j++)printf "%s", a[int(4*rand())];print ""}}' > mystringfile.txt

然后是脚本:

$ awk '
NR==FNR {                              # process the sequence file
    l=length($1)-15                    # length-15 16 byte strings coming up
    for(i=1;i<=l;i++) {                # using l as variable name is stupid
        s=substr($0,i,16)              
        a[s]=a[s] (a[s]==""?"":",") i  # hash string start indexes to a, string as key
    }                                  # a["ACTGTGCACGTATAGC"]=3,141,592
    next
}                                      # the search part 
$1 in a {                              # if search string is found
    print a[$1], $1                    # output index and string
}' sequence.txt mystringfile.txt

将 2400000 字节 sequence.txt 存储到哈希中花费了 13 秒,并在我的迷你笔记本电脑上使用了 721 MB 的内存。整个脚本运行了 35 秒,找到了大约 17000 次点击。

【讨论】:

  • 这速度快得离谱!谢谢!
  • @rororo 谢谢。该评论直接进入我的简历。 :D
猜你喜欢
  • 2019-04-16
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-22
  • 2011-12-16
相关资源
最近更新 更多