대용량 필터를 사용하여 대용량 파일 필터링

대용량 필터를 사용하여 대용량 파일 필터링

$file1에 저장하기 위해 추출하고 싶습니다 $file2.

$file1크기는 4GB이고 행 수는 약 2천만 개 이며 크기 $file2는 약 140MB이며 ,두 파일의 최대 행 길이는 1000보다 훨씬 낮습니다. 문자 .LC_ALL=C$file1\0

이 명령을 예상하지 못했습니다

parallel --pipepart -a $file1 grep -Ff $file2

많은 메모리를 소비하고 운영 체제에 의해 종료됩니다.

스레드 수를 제한하면 다음 명령이 작동합니다.

parallel --pipepart -j 8 -a $file1 grep -Ff $file2

마지막 명령의 경우 htop은 각 grep -Ff $file2스레드가 계속해서 12.3GB의 메모리를 소비한다는 것을 보여줍니다. 나는 이 요구사항이 grep 에 의해 구축된 사전에서 나온 것이라고 가정합니다 $file2.

이러한 필터를 보다 효율적으로 구현하려면 어떻게 해야 합니까?

답변1

그것은 덮여있다man parallel https://www.gnu.org/software/parallel/man.html#예: -Grepping-n-lines-for-m-regular-expressions

예: n줄에서 m개의 정규식을 찾습니다.

정규식이 많은 대용량 파일을 grep하는 가장 간단한 솔루션은 다음과 같습니다.

grep -f regexps.txt bigfile

또는 정규식이 고정 문자열인 경우:

grep -F -f regexps.txt bigfile

CPU, RAM, 디스크 I/O라는 3가지 제한 요소가 있습니다.

RAM은 측정하기 쉽습니다. grep 프로세스가 사용 가능한 메모리의 대부분을 차지하는 경우(예: top 실행 시) RAM은 제한 요소입니다.

CPU는 측정하기도 쉽습니다. grep이 CPU의 90%를 초과하는 경우 CPU가 제한 요소이므로 병렬화가 속도를 높입니다.

디스크 I/O가 제한 요소인지 확인하기 어렵고 디스크 시스템에 따라 병렬화가 더 빨라질 수도 있고 느려질 수도 있습니다. 확실히 알 수 있는 유일한 방법은 테스트하고 측정하는 것입니다.

제한 요소: 메모리

큰 파일에 대한 일반 grep -f regexs.txt는 크기에 관계없이 작동하지만 regexps.txt가 너무 커서 메모리에 맞지 않으면 분할해야 합니다.

grep -F에는 약 100바이트의 RAM이 필요한 반면, grep에는 정규 표현식 1바이트당 약 500바이트의 RAM이 필요합니다. 따라서 regexps.txt가 RAM의 1%를 차지한다면 아마도 너무 큰 것입니다.

정규식을 고정 문자열로 변환할 수 있다면 그렇게 하십시오. 예를 들어, 대용량 파일에서 모두 다음과 같은 줄을 찾는 경우:

ID1 foo bar baz Identifier1 quux
fubar ID2 foo bar baz Identifier2

그런 다음 regexps.txt를 다음에서 변환할 수 있습니다.

ID1.*Identifier1   
ID2.*Identifier2

입력하다:

ID1 foo bar baz Identifier1
ID2 foo bar baz Identifier2

이렇게 하면 약 80% 더 적은 메모리를 사용하고 더 빠른 grep -F를 사용할 수 있습니다.

그래도 메모리에 맞지 않으면 다음을 수행할 수 있습니다.

parallel --pipepart -a regexps.txt --block 1M grep -Ff - -n bigfile |
  sort -un | perl -pe 's/^\d+://'

1M은 사용 가능한 메모리를 CPU 스레드 수(grep -F의 경우 200, 일반 grep의 경우 1000)로 나눈 값입니다. GNU/Linux에서는 다음을 수행할 수 있습니다:

free=$(awk '/^((Swap)?Cached|MemFree|Buffers):/ { sum += $2 }
          END { print sum }' /proc/meminfo)
percpu=$((free / 200 / $(parallel --number-of-threads)))k

parallel --pipepart -a regexps.txt --block $percpu --compress \
  grep -F -f - -n bigfile |
  sort -un | perl -pe 's/^\d+://'

중복된 줄과 잘못된 순서를 허용할 수 있다면 다음을 수행하는 것이 더 빠릅니다.

parallel --pipepart -a regexps.txt --block $percpu --compress \
  grep -F -f - bigfile

제한 요소: CPU

CPU가 제한 요소인 경우 정규식을 병렬화해야 합니다.

cat regexp.txt | parallel --pipe -L1000 --round-robin --compress \
  grep -f - -n bigfile |
  sort -un | perl -pe 's/^\d+://'

이 명령은 CPU당 grep을 시작하고 CPU당 한 번 큰 파일을 읽습니다. 그러나 이 작업은 병렬로 수행되므로 첫 번째 읽기를 제외한 모든 읽기가 RAM에 캐시됩니다. regexp.txt의 크기에 따라 -L1000 대신 --block 10m을 사용하는 것이 더 빠를 수도 있습니다.

일부 스토리지 시스템은 여러 블록을 병렬로 읽을 때 성능이 더 좋습니다. 이는 일부 RAID 시스템 및 일부 네트워크 파일 시스템에 해당됩니다. 대용량 파일을 병렬로 읽기:

parallel --pipepart --block 100M -a bigfile -k --compress \
  grep -f regexp.txt

그러면 빅파일이 100MB 청크로 분할되고 각 청크에 대해 grep이 실행됩니다. bigfile과 regexp.txt를 병렬로 읽으려면 --fifo를 사용하여 두 가지를 결합하십시오.

parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
  \| parallel --pipe -L1000 --round-robin grep -f - {}

두 개 이상의 정규 표현식과 일치하면 한 줄이 중복될 수 있습니다.

더 큰 문제

문제가 너무 커서 해결할 수 없다면 아마도 Lucene을 사용할 준비가 된 것입니다.

관련 정보