在大文件循环中使用“split”的脚本

在大文件循环中使用“split”的脚本

我正在尝试让 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 命令来读入它,然后将其作为行数传递给 split 。它的格式类似于:

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.5GB 猛增到 16GB,并且变得极其无响应。它对于前两个文件可以正常工作,并且可以按照我想要的方式分割它们。有什么建议吗?

编辑:源文件全部约为 200-300MB。当我直接对任何文件运行 split 命令时,它工作正常。有 30 个文件需要以这种方式拆分,C21、C22、C23 等。我重新运行脚本,它在这次达到内存限制之前完成了前 10 个文件。

编辑2:所以,我做了一些非常严厉的工作。通过三个文件运行后,我简单地把

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

我注意到在 split 命令之后,我使用的内存会根据 free -m 急剧增加,当关闭我运行 split 命令的终端窗口时,峰值也不会消失。我相信我的系统上的光盘缓存配置存在一些问题:Linux 必须缓存我正在写入或写入的文件并且不进行清理。当将此脚本运行到每个第三个文件时,该脚本会运行所有文件,尽管速度相对较慢,并且我的系统在此后保持稳定。我怀疑这种缓存也可能与我正在开发的 NTFS 文件系统有关。

答案1

基于 NTFS 文件系统性能的附加说明

在写完这个答案的下半部分后,OP 指出该脚本正在 NTFS 磁盘上运行,并怀疑这可能是问题的一部分。

这并不奇怪:NTFS 存在与处理许多小文件特别相关的性能问题。我们正在为每个输入文件创建数百万个小文件。

因此,不良的 NTFS 性能可能是性能下降的另一种解释,而内存的极端使用似乎仍然与 mmap() 有关。

NTFS 性能不佳
配置 NTFS 文件系统以提高性能


通过大量使用 mmap() 来解释内存问题

脚本中出现的内存问题split似乎与“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


根据示例,为了粗略估计要处理的文件,
我们假设输入文件为 300MB,输出文件为 100B:

这给了我们大约 3000000 个要写入的文件。我们一次只写一篇。但我们使用mmap().这意味着,对于每个文件,至少使用一个内存页,其大小为 4096 B。

考虑到这一点,我们触摸约12GB内存(1) 对于一个输入文件(但不是一次输入所有文件)。 300 万个文件和 12 GB 听起来可能会给内核带来一些工作。

基本上,看起来split只是不适合这份工作,因为它使用映射()
在其他情况下这是一件好事。
但在这种极端的输入情况下,它会严重扰乱内存管理——然后需要一些时间来清理。 (2)

(2)它不会真正同时使用太多内存,而是在短时间内mmap大量小文件。

(1) 还是仅地址空间?

答案2

我很好奇用问题的解决方案来解决内存问题split,但独立地,这种替代方法可能有用:

您可以使用csplit而不是split分割这种文件。

对于csplit,您需要定义一个模式来定义分割位置,并且您可以使用带有单个数字的行作为分隔符 - 如果您知道注释中没有这样的行。

我不清楚内存问题是什么,但使用不同的工具可能会解决它。

但还有一个好处就是命令变得更简单,不需要先获取数字。

该命令类似于:

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

相关内容