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.5GB에서 16GB로 증가하고 응답이 극도로 느려집니다. 처음 두 파일에 대해서는 잘 작동하고 원하는 방식으로 분할합니다. 어떤 제안이 있으십니까?
편집: 소스 파일은 모두 약 200-300MB입니다. 어떤 파일에서든 직접 분할 명령을 실행하면 제대로 작동합니다. 이런 방식으로 분할해야 하는 파일은 C21, C22, C23 등 30개입니다. 스크립트를 다시 실행하여 이번에는 메모리 제한에 도달하기 전에 처음 10개 파일을 완료했습니다.
편집 2: 그래서 꽤 무거운 작업을 수행했습니다. 세 개의 파일을 실행한 후 간단히
echo 3 | tee /proc/sys/vm/drop_caches
나는 분할 명령 후에 내가 사용한 메모리가 free -m에 따라 극적으로 증가하고 분할 명령을 실행한 터미널 창을 닫을 때 스파이크가 사라지지 않는다는 것을 발견했습니다. 내 시스템의 디스크 캐시 구성에 몇 가지 문제가 있다고 생각합니다. Linux는 내가 쓰고 있는 파일을 캐시하고 정리하지 않아야 합니다. 세 번째 파일마다 이 스크립트를 실행할 때 스크립트는 상대적으로 느리긴 하지만 모든 파일을 실행하며 이후 시스템은 안정적으로 유지됩니다. 이 캐싱은 제가 개발 중인 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
예제에 따르면 처리할 파일의 대략적인 추정치를 제공하기 위해
입력 파일이 300MB이고 출력 파일이 100B라고 가정해 보겠습니다.
이렇게 하면 약 3,000,000개의 파일에 쓸 수 있습니다. 한 번에 하나의 기사를 작성하겠습니다. 그러나 우리는 mmap()
.
이를 염두에 두고 우리는터치 약 12GB 메모리(1) 하나의 입력 파일에 대해(그러나 모든 파일을 동시에 사용하지는 않음). 3백만 개의 파일과 12GB는 커널에 약간의 작업을 제공할 수 있을 것 같습니다.
기본적으로는 split
그냥 그런 것 같아요이 직업에는 적합하지 않습니다, 사용하기 때문에지도().
다른 경우에는 이것이 좋은 일입니다.
그러나 이와 같은 극단적인 입력 상황에서는 메모리 관리가 심각하게 엉망이 될 수 있으며 정리하는 데 시간이 걸릴 수 있습니다. (2)
(2) 실제로 동시에 너무 많은 메모리를 사용하지는 않지만 오히려 짧은 시간에 많은 작은 파일을 mmap합니다.
(1) 아니면 주소 공간만?
답변2
메모리 문제를 해결하기 위해 질문에 대한 솔루션을 사용하는 것에 대해 궁금합니다. split
그러나 독립적으로 이 대안이 작동할 수 있습니다.
csplit
이런 종류의 파일을 분할하는 대신 사용할 수 있습니다 split
.
의 경우 csplit
분할할 위치를 정의하는 패턴을 정의해야 하며 주석에 해당 줄이 없다는 것을 알고 있는 경우 단일 숫자가 있는 줄을 구분 기호로 사용할 수 있습니다.
메모리 문제가 무엇인지 정확히 모르지만 다른 도구를 사용하면 문제가 해결될 수 있습니다.
그러나 또 다른 이점은 명령이 더 간단해지고 번호를 먼저 가져올 필요가 없다는 것입니다.
명령은 다음과 유사합니다.
csplit --elide-empty-files -n4 in.txt '/^[0-9]\+$/' '{*}'