Grep -E, Sed -E - ประสิทธิภาพต่ำเมื่อใช้ '[x] {1,9999}' แต่ทำไม?


9

เมื่อgrepหรือsedใช้กับตัวเลือก--extended-regexpและรูปแบบ{1,9999}เป็นส่วนหนึ่งของ regexp ที่ใช้ประสิทธิภาพของคำสั่งเหล่านี้จะต่ำ เพื่อให้ชัดเจนยิ่งขึ้นมีการทดสอบด้านล่างสองสามข้อ [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

$ time 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
นั่นเป็นข้อสังเกตที่น่าสนใจ - ฉันเดาว่าคุณจะต้องขุดลึกเข้าไปใน internals ของ grep เพื่อค้นหาว่ามันสร้างต้นไม้แยก (มันน่าสนใจที่จะเปรียบเทียบ[0-9]+เช่นกัน)
steeldriver

3
อินพุตไม่สำคัญ ในฐานะที่เป็น @steeldriver แนะนำการชะลอตัวนำหน้าการจับคู่ การทดสอบที่ง่ายคือเมื่อเทียบกับtime grep -E '[0-9]{1,99}' </dev/null time grep -E '[0-9]{1,9999}' </dev/nullแม้ว่าจะไม่มีอินพุตคำสั่งที่สองก็ช้า (ในวันที่ 16.04) ตามที่คาดไว้การละเว้น-Eและการหลบหนี{และการ}ทำงานเหมือนเดิมและการแทนที่-Eด้วย-Pจะไม่ช้า (PCRE เป็นเครื่องมือที่แตกต่างกัน) ที่น่าสนใจที่สุดเป็นเท่าใดได้เร็ว [0-9]กว่า., และแม้กระทั่งx [0123456789]กับใด ๆ ของเหล่านั้นและ{1,9999}, grepกินเป็นจำนวนมากของ RAM; ฉันไม่กล้าปล่อยให้มันวิ่งนานกว่า ~ 10 นาที
Eliah Kagan

3
@ αғsнιηไม่มีที่{ }จะ' 'ยกมา ; grepเปลือกผ่านพวกเขาเปลี่ยนแปลงไป อย่างไรก็ตาม{1,9999}จะเป็นอย่างรวดเร็วและง่ายขยายตัวรั้ง 1 9999เปลือกก็จะขยายไปยัง
Eliah Kagan

4
@ αғsнιηฉันไม่ทราบว่าคุณหมายถึงอะไร แต่นี่ไม่เกี่ยวข้องกับเปลือก ระหว่างคำสั่งที่ใช้เวลานานฉันใช้psและtopเพื่อตรวจสอบว่าgrepผ่านข้อโต้แย้งที่คาดไว้และไม่ได้bashใช้ RAM และ CPU จำนวนมาก ฉันคาดหวังgrepและsedทั้งสองใช้ฟังก์ชั่น POSIX regexนำมาใช้ในlibcสำหรับการจับคู่ BRE / ERE; ฉันไม่ควรพูดคุยเกี่ยวกับgrepการออกแบบโดยเฉพาะยกเว้นในกรณีที่grepนักพัฒนาเลือกที่จะใช้ไลบรารีนั้น
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

จำนวนของเอกสารทั้งหมดนั้นมีความสัมพันธ์กับจำนวนการวนซ้ำโดยประมาณ แต่หน่วยความจำที่จัดสรรนั้นดูเหมือนจะเพิ่มขึ้นแบบทวีคูณ

นั่นเป็นวิธีการใช้ regexps ของ GNU หากคุณคอมไพล์ GNU grepด้วยCPPFLAGS=-DDEBUG ./configure && makeและเรียกใช้คำสั่งเหล่านั้นคุณจะเห็นเอฟเฟกต์แบบเอ็กซ์โปเนนเชียล การลงลึกไปกว่านั้นจะหมายถึงการผ่านทฤษฎีมากมายเกี่ยวกับ DFA และดำน้ำในการนำ gnulib regexp ไปปฏิบัติ

ที่นี่คุณสามารถใช้แทน PCREs ที่ไม่ได้ดูเหมือนจะมีปัญหาเดียวกัน: 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 -Eแต่ในawkนอกจากนี้ยังมีผลการดำเนินงานต่ำ (ประมาณคำสั่ง awk ที่ผ่านมา) อาจawkไม่สามารถใช้ PCRE ด้วยหรือ
αғsнιη
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.