while 루프에서 grep 명령을 사용할 때 병렬성을 사용하는 방법

while 루프에서 grep 명령을 사용할 때 병렬성을 사용하는 방법

모든 검색 문자열이 포함된 파일이 있습니다. 해당 파일에서 모든 문자열을 가져와서 하나씩 다른 파일에 저장하고 있습니다. 이제 시간이 너무 오래 걸립니다. 이 파일에서 병렬 명령을 어떻게 구현할 수 있습니까?

while read line; do
line2=`grep -w "$line" $file2`

if [[ ! -z $line2 ]]
then
   echo "$line: present" >> exclusion_list_$$.txt
   echo "$line2" >> exclusion_list_$$.txt

   echo "grep line $line2 "
fi
done < exclusion.txt 

나는 모든 내부 while 명령을 함수에 넣고 해당 함수를 병렬로 호출할 수 있을 것이라고 생각했습니다.

나는 이것에 익숙하지 않습니다. 이것이 올바른 방법인지 아니면 다른 효율적인 방법이 있는지 알려주십시오.

답변1

당신의 문제는 다음과 같습니다 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가 제한 요소인지 확인하기 어렵고 디스크 시스템에 따라 병렬화가 더 빨라질 수도 있고 느려질 수도 있습니다. 확실히 알 수 있는 유일한 방법은 테스트하고 측정하는 것입니다.

제한 요소: RAM

큰 파일에 대한 일반 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 --roundrobin --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 --roundrobin grep -f - {}

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

더 큰 문제

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

답변2

여러분은 GNU 병렬성에 대해 들어보셨을 것입니다. 여기서는 작동하지 않습니다 ...

병렬화를 활용하려면 거대한 파일이어야 하는데 bash는 그렇게 할 수 없습니다. C나 다른 실제 프로그래밍 언어로 전환해야 합니다.

귀하의 코드는 다음을 충족해야 합니다.

  • 파일의 길이 L 결정
  • X 프로세스로 포크
  • 그들은 모두 파일의 n*X_n 비트부터 읽기 시작해야 합니다.
  • L/X 비트를 읽은 후 중지해야 함
  • 읽은 섹션을 확인하세요.
  • IPC를 사용한 endfile의 동기식 쓰기

이는 확실히 가능하지만 이 옵션을 고려할 가치가 있으려면 파일 길이가 수 테라바이트가 되어야 합니다.

관련 정보