大容量ファイルループで「分割」を使用するスクリプト

大容量ファイルループで「分割」を使用するスクリプト

bashスクリプトを操作しようとしていますが、途中でひどいエラーが発生します。このスクリプトの目的は、複数の大きなテキストファイルを複数のファイルに分割し、ファイルから行数を読み取ることです。

#!/bin/bash
DIR="$( cd "$( dirname "$0" )" && pwd )" 
for i in 21 22 23 24 25 26 27 28 29 30 33 34 35 36 37 38 39 210 211 212 213 214 215 216 217 218 310 311 312 313 314 315 316 317 318
do
    lines="`head -1 $DIR/C$i/DOPC-C$i.xyz`"
    echo $lines
    lines=$((lines+2))
    split -a4 -d -l$lines $DIR/C$i/DOPC-C$i.xyz $DIR/C$i/DOPC-C$i-
done

大きなテキストファイルの最初の行は分子番号であるため、headコマンドを使用してそれを読み取り、行番号に分割に渡します。形式は次のようになります。

3
Comment
C 0.41238 0.2301928 0.123123
H 0.123123 0.123233 0.5234234
H 0.123123 0.123233 0.5234234
3
Comment
C 0.41238 0.2301928 0.123123
H 0.123123 0.123233 0.5234234
H 0.123123 0.123233 0.5234234

ただし、端末でこのプログラムを実行すると、システムメモリ使用量がfree -mから1.5 GBから16 GBに増加し、応答が非常に遅くなります。最初の2つのファイルにはうまく機能し、必要な方法で分割します。どんな提案がありますか?

編集:ソースファイルはすべて約200〜300 MBです。任意のファイルから直接分割コマンドを実行すると正常に動作します。このように分割する必要があるファイルは、C21、C22、C23など30個です。スクリプトを再実行して、今回はメモリ制限に達する前に最初の10個のファイルを完了しました。

編集2:だからかなり重い仕事をしました。 3つのファイルを実行した後、単に

echo 3 | tee /proc/sys/vm/drop_caches

私は分割コマンドの後、私が使用したメモリがfree -mに従って劇的に増加し、分割コマンドを実行したターミナルウィンドウを閉じたときにスパイクが消えないことを発見しました。私のシステムのディスクキャッシュ構成にはいくつかの問題があると思います。 Linuxは、私が書いているファイルをキャッシュして整理してはいけません。 3番目のファイルごとにこのスクリプトを実行すると、スクリプトは比較的遅いですが、すべてのファイルを実行し、その後システムは安定しています。このキャッシュは、私が開発しているNTFSファイルシステムとも関連があると考えられています。

答え1

NTFS ファイルシステムのパフォーマンスに関する追加の注意

この回答の後半を作成した後、OPはスクリプトがNTFSディスク上で実行されていることを指摘し、これが問題の一部である可能性があると疑いました。

これは驚くべきことではありません。 NTFSには、特に多くの小さなファイル処理に関連するパフォーマンスの問題があります。私たちは、各入力ファイルに対して何百万もの小さなファイルを生成しています。

したがって、低いNTFSパフォーマンスはパフォーマンスの低下に関する別の説明であり、極端なメモリ使用量は依然としてmmap()に関連しているようです。

NTFSのパフォーマンスが悪い。
パフォーマンスを向上させるためのNTFSファイルシステムの構成


mmap() の広範な使用によって記述されるメモリの問題

スクリプトのメモリ問題は、split「分割」でのmmapの使用に関連しているようです。

strace各出力ファイルに対して、次の呼び出しが表示されます。

28892 open("xx02", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
28892 fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
28892 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821582f000
28892 write(3, "sometext\n", 30) = 30
28892 close(3)                          = 0
28892 munmap(0x7f821582f000, 4096)      = 0


例では、処理するファイルのおおよその見積もりを提供するために、
入力ファイルが300 MBで、出力ファイルが100 Bであるとします。

これにより、約3,000,000個のファイルに書き込むことができます。一度に1つの記事を作成します。しかし、我々はmmap()

これを念頭に置いて、私たちはタッチ約12GBメモリ(1)1つの入力ファイルに対して(ただし、すべてのファイルを同時に使用するわけではありません)。 300万個のファイルと12GBはカーネルにいくつかのタスクを提供できるようです。

基本的にはsplitただそうだと思います。この職業には適していません、使用するため地図()
他のケースでは、これは良いことです。
しかし、このような極端な入力状況では、メモリ管理が深刻に混乱し、クリーンアップに時間がかかることがあります。 (2)

(2)実際には同時にメモリをあまり使用しませんが、むしろ短時間で多くの小さなファイルをmmapします。

(1)またはアドレス空間のみ?

答え2

メモリの問題を解決するために質問に解決策を使用することについて疑問に思います。splitただし、独立してこの選択肢が機能する可能性があります。

csplitこの種のファイルを分割する代わりに使用できますsplit

の場合、csplit分割する位置を定義するパターンを定義する必要があり、コメントに対応する行がないことがわかっている場合は、単一の数字を含む行を区切り文字として使用できます。

メモリの問題は何であるか正確にはわかりませんが、他のツールを使用すると問題が解決する可能性があります。

しかし、別の利点は、コマンドがより簡単になり、番号を最初に取得する必要がないことです。

コマンドは次のようになります。

csplit --elide-empty-files -n4 in.txt '/^[0-9]\+$/' '{*}'

関連情報