Grep -E, Sed -E- '[x] {1,9999}'를 사용할 때 성능이 저하되는 이유는 무엇입니까?


9

grep또는 sed옵션과 함께 사용 --extended-regexp하고 패턴이 {1,9999}사용되는 정규 표현식의 일부입니다,이 명령의 성능이 낮은된다. 좀 더 명확하게하기 위해, 몇 가지 테스트가 적용되었습니다. [1] [2]

  • 상대의 성능 grep -E, egrep및은 sed -E으로 만들어진 때문에 시험 만, 거의 동일 grep -E제공된다.

시험 1

$ time grep -E '[0-9]{1,99}' < /dev/null

real    0m0.002s

시험 2

$ time grep -E '[0-9]{1,9999}' < /dev/null

> real    0m0.494s

시험 3

$ 시간 grep -E '[0123456789] {1,9999}'</ dev / null

> 실제 21m43.947s

시험 4

$ time grep -E '[0123456789]+' < /dev/null
$ time grep -E '[0123456789]*' < /dev/null
$ time grep -E '[0123456789]{1,}' < /dev/null
$ time grep -P '[0123456789]{1,9999}' < /dev/null

real    0m0.002s       

이 성능 차이가 큰 이유는 무엇입니까?


3
즉 흥미로운 관찰입니다 - 내 생각 엔 당신은 (그것을 비교하는 흥미로운 일이 될 것이다는 파스 트리를 구축 정확히 어떻게 찾을 그렙의 내부로 깊이 파고해야 할 것입니다 [0-9]+뿐만 아니라)
steeldriver

3
입력은 중요하지 않습니다. @steeldriver가 제안했듯이 속도 저하 일치합니다. 더 간단한 테스트는 time grep -E '[0-9]{1,99}' </dev/nulltime grep -E '[0-9]{1,9999}' </dev/null. 입력이 없더라도 두 번째 명령은 느립니다 (16.04에서). 예상대로, 생략 -E및 탈출 {}동일한으로 동작 및 교체 -E와하면 -P(PCRE 다른 엔진) 느린 아니다. 가장 흥미로운 얼마나 많이 빨리 [0-9] 보다 ., x심지어 [0123456789]. 그 중 하나와 함께 {1,9999}, grepRAM의 엄청난 금액을 소비; 나는 ~ 10 분 이상 감히하지 않았다.
Eliah Kagan

3
@ 아니 αғsнιη의가 { }있다 ' '인용 ; 쉘은 변경되지 않은 채로 전달합니다 grep. 어쨌든 {1,9999}매우 빠르고 간단한 괄호 확장 이 될 것 입니다. 쉘은 단지 확장 할 것이다 1 9999.
Eliah Kagan

4
@ αғsнιη 나는 당신이 무엇을 의미하는지 잘 모르지만, 이것은 분명히 껍질과 관련이 없습니다. 오래 실행되는 명령 중에 예상 인수가 전달되었고 사용 하지 않았는지 확인 ps하고 사용하는 데 많은 RAM과 CPU가 소비 top되는지 확인 grep했습니다 bash. BRE / ERE 일치를 위해 libc 로 구현 된 POSIX 정규식 함수를 기대 grep하고 sed있습니다 . 개발자가 해당 라이브러리를 사용하기로 선택한 경우를 제외하고 는 디자인 에 대해 구체적으로 이야기해서는 안됩니다 . grepgrep
Eliah Kagan

3
테스트를로 대체하여 time grep ... < /dev/null사람들이 실제 문제를 grep다른 데이터와 관련된 데이터와 혼동하지 않도록하는 것이 좋습니다.
muru

답변:


10

시간이 걸리는 일치하는 것이 아니라 RE의 구축입니다. 꽤 많은 RAM을 사용한다는 것을 알 수 있습니다.

$ valgrind grep -Eo '[0-9]{1,9999}' < /dev/null
==6518== HEAP SUMMARY:
==6518==     in use at exit: 1,603,530,656 bytes in 60,013 blocks
==6518==   total heap usage: 123,613 allocs, 63,600 frees, 1,612,381,621 bytes allocated
$ valgrind grep -Eo '[0-9]{1,99}' < /dev/null
==6578==     in use at exit: 242,028 bytes in 613 blocks
==6578==   total heap usage: 1,459 allocs, 846 frees, 362,387 bytes allocated
$ valgrind grep -Eo '[0-9]{1,999}' < /dev/null
==6594== HEAP SUMMARY:
==6594==     in use at exit: 16,429,496 bytes in 6,013 blocks
==6594==   total heap usage: 12,586 allocs, 6,573 frees, 17,378,572 bytes allocated

할당 수는 반복 횟수에 대략 비례하는 것으로 보이지만 할당 된 메모리는 기하 급수적으로 증가하는 것으로 보입니다.

그것은 GNU 정규 표현식이 어떻게 구현되는지에 달려 있습니다. 당신은 GNU를 컴파일 할 경우 grepCPPFLAGS=-DDEBUG ./configure && make, 그 명령을 실행, 당신은 행동 지수 효과를 볼 수 있습니다. 그것보다 더 깊이 들어가면 DFA에 대한 많은 이론을 겪고 gnulib 정규 표현식 구현에 뛰어들 것입니다.

여기서 동일한 문제가없는 것처럼 PCRE를 대신 사용할 수 있습니다. grep -Po '[0-9]{1,65535}'(최대 [0-9](?:[0-9]{0,10000}){100}1, 1,000,001 반복 과 같은 작업을 수행 할 수는 있지만 최대 시간)보다 시간이나 메모리가 오래 걸리지 않습니다 grep -Po '[0-9]{1,2}'.


이 문제를 해결할 방법이 있습니까?
Sergiy Kolodyazhnyy

3
@SergiyKolodyazhnyy, grep -Po '[0-9]{1,9999}문제가없는 것으로 사용할 수 있습니다 .
Stéphane Chazelas

1
이 글은 단지의 sed -E또는 grep -Eawk이 낮은 성능이 (마지막 awk 명령에 대한 참조). 어쩌면 awk또한 PCRE를 사용하지 않을 수 있습니까?
αғsнιη
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.