whileループを入力するためにgrepを使用するBashスクリプトの速度を向上させます。

whileループを入力するためにgrepを使用するBashスクリプトの速度を向上させます。

次のシナリオを使用して、多数の行(> 500 Mb)で構成されているファイルに対してこのスクリプトが機能するようにします。

odd lines: >BLA_BLA lenght_XX cov.XX
even lines: AGCAGCAGACTCAGACTACAGAT  # on even lines there's a DNA sequence

その機能は、「cov」以降の値を再計算することです。引数として渡された引数を使用し、前の引数を置き換え、「G」と「C」がDNA配列に入る偶数行の割合を計算します。

したがって、出力は次のようになります。

> BLA_BLA lenght_XX
> nucleotidic_cov XX
> DNA seq (the same of even lines)
> GC_CONT: XX

コードは次のとおりです(ループのみ)。

K=$(($READLENGHT - $KMER + 1))
Y=$(echo "scale=4; $K / $READLENGHT" | bc)

while read odd; do
    echo -n "${odd##}" | cut -d "_" -f 1,2,3,4 && printf "nucleotide_cov: " 
    echo "scale=4;${odd##*_} / $Y" | bc 
    read even
    echo "${even##}" &&
    ACOUNT=$(echo "${even##}" |  sed -e "s/./&\n /g" | grep -c "A")  
    GCOUNT=$(echo "${even##}" |  sed -e "s/./&\n /g" | grep -c "G")
    CCOUNT=$(echo "${even##}" |  sed -e "s/./&\n /g" | grep -c "C")
    TCOUNT=$(echo "${even##}" |  sed -e "s/./&\n /g" | grep -c "T")
    TOTALBASES=$(($ACOUNT+$GCOUNT+$CCOUNT+$TCOUNT))
    GCCONT=$(($GCOUNT+$CCOUNT))
    printf "GC_CONT: " 
    echo "scale=2;$GCCONT / $TOTALBASES *100" | bc  
done < "$1"

16コアサーバーで大容量テキストファイル(500 Mb以上)を実行すると、非常に遅くなります。このスクリプトの速度を向上させる方法についてのアイデアはありますか?

編集する

要求に応じて必要なI / OはPastebinを介して提供されます。https://pastebin.com/FY0Z7kUW

答え1

シェルで合理的に実行できる操作の制限に達しました。 AWK、Perl、Pythonなどの言語でスクリプトを再構築する必要があります。これらの高度な言語を使用すると、すべてのテキスト処理に対して複数のプロセスを実行する必要がなくなり、そのために組み込み機能を使用できます。

答え2

パーセンテージの計算は、次のような単一タスクに縮小できます。

 echo "${even##}" | awk '{x=gsub(/[ACT]/,""); y=gsub(/G/,""); printf "GC_CONT : %.2f%%\b", (y*100)/(x+y) }'

gsubはパターンを置き換え、パターンが置き換えた回数を返します。これは、パーセンテージをすばやく計算するために使用できます。

awkでは、奇数行と偶数行を処理することもできます。奇妙な行で何をしているかはわかりませんが、全体の機能をawkに入れることができます。

awk -F '_' -v Y="$Y" '{ if(NR%2==1) {
    printf "%s %s %s %s %s\nnucleotidic_cov : %.4f\n",$1,$2,$3,$4,$5, ($6 / Y)
} else {
    x=gsub(/[AT]/,""); 
    y=gsub(/[GC]/,""); 
    printf "GC_CONT : %.2f%%\n", (y*100)/(x+y)
    }
 }' large_file

編集する:OPの要求に応じて奇数行のifブロックを変更しました。 gsub は "cov" を削除します。数字で判断します。シェル変数 $Y を awk に渡した後、必要な形式に分割して印刷できるようになりました。

複数のタスクの代わりに単一のawkスクリプトを使用すると、タスク速度が大幅に向上します。

答え3

プログラムが並列化されていない場合(あまりにも多く)、コア数は重要ではありません。

sed と grep の代わりにwcand を使用すると、作業速度が速くなる可能性があります。tr

ACOUNT=$(echo "${even##}" | tr -d [^A] | wc -m)

しかし、実際に最も大きな問題は、シェルが迅速で汚れた作業のためにプログラムするのは簡単ですが、生の処理能力の点で作業に適したツールではないことです。スレッド機能を備えたPerlやPythonなどのより複雑なプログラミング言語を使用することをお勧めします(したがって、すべてのコアを使用できます)。

Perl では、次のようにこれを行うことができます。

#!/usr/bin/perl -w
use strict;
use warnings;

my $y = ...;                              # calculate your Y value here
while(my $odd = <ARGV>) {                 # Read a line from the file(s) passed
                                          # on the command line
    chomp $odd;                           # lose the newline
    my @split = split /_/, $odd;          # split the read line on a "_" boundary
                                          # into an array
    print join("_", @split[0..3]) . "\n"; # print the first four elements of the
                                          # array, separated by "_"
    print $split[$#split] / $y . "\n";    # Treat the final element of the
                                          # @split array as a number, divide it
                                          # by $y, and output the result
    my %charcount = (                     # Initialize a hash table
        A => 0,
        G => 0,
        C => 0,
        T => 0
    );
    my $even = <ARGV>;                    # read the even line
    chomp $even;
    foreach my $char(split //,$even) {    # split the string into separate
                                          # characters, and loop over them
        $charcount{$char}++;              # Count the correct character
    }
    my $total = $charcount{A} + $charcount{G} + $charcount{C} + $charcount{T};
    my $gc = $charcount{G} + $charcount{C};
    my $perc = $gc / $total;
    print "GC_CONT: $perc\n";             # Do our final calculations and
                                          # output the result
}

注:テストされていません(「Perlはこのコードを受け入れますか」を除く)。

Perlについてもっと詳しく知りたい場合は、実行して起動しperldoc perlintroてください;-)

答え4

長いファイルを1行ずつ読み、各反復で複数のコマンドを実行しています。直面する主な問題は、これらの計算を実行し、一度に非常に小さなファイルチャンクを読み取るのに遅延があることです。

Stephen Kittの答えは素晴らしいです。ファイルの内容をキャッシュし、文字列操作をより効率的に実行できる高レベルの言語で書き直したいと思います。

ストレージとファイルシステムのパフォーマンスを除外するには、次のコマンドを使用してRAMからファイルをロードできます。

# mkdir /mnt/tmpfs
# mount -t tmpfs -o size=1024M tmpfs /mnt/tmpfs
# cp <input_file> /tmp/tmpfs
# <script> /tmp/tmpfs/<input_file>

I / Oが制限されているため、プロセスが高速になります。しかし、C、Ruby、またはPythonで書き直すと決して良くなることはありません。

関連情報