grep ทำงานเร็วแค่ไหน?


113

ฉันรู้สึกทึ่งมากกับการทำงานของ GREP ในเชลล์ก่อนหน้านี้ฉันเคยใช้วิธีการสตริงย่อยใน java แต่ตอนนี้ฉันใช้ GREP และดำเนินการในเวลาไม่กี่วินาทีมันเร็วกว่าโค้ดจาวาที่ฉันเคยเขียนอย่างเห็นได้ชัด (ตามประสบการณ์ของฉันฉันอาจจะคิดผิด)

ที่บอกว่าฉันไม่สามารถเข้าใจได้ว่ามันเกิดขึ้นได้อย่างไร? นอกจากนี้ยังมีไม่มากในเว็บ

ใครสามารถช่วยฉันด้วยเรื่องนี้?


5
เป็นโอเพ่นซอร์สเพื่อให้คุณสามารถมองหาตัวเองได้ gnu.org/software/grep/devel.html
driis

6
ปลาไร้สาระมีการเขียนที่ยอดเยี่ยมที่ตอบคำถามของคุณได้อย่างแน่นอน: ridiclesfish.com/blog/posts/old-age-and-treachery.html
David Wolever

@WilliamPursell เมื่อเวลาดำเนินการผ่านไปในไม่กี่วินาที JIT อาจอุ่นขึ้นและความแตกต่างที่ทำให้มึนงงเกิดจาก (1) grep ฉลาดอย่างไม่น่าเชื่อเกี่ยวกับสิ่งที่มันทำและ (2) โค้ด Java ทำให้ตัวเลือกอัลกอริทึมแย่มาก สำหรับปัญหาเฉพาะ grep มุ่งเน้นไปที่

3
การติดตั้ง Java ของคุณใช้เวลาในการเริ่มต้น JVM นานเท่าใดและใช้เวลาในการรันโค้ดของคุณนานเท่าใด หรืออาจเป็นเรื่องของอัลกอริทึมที่คุณใช้ในโค้ด Java ของคุณ อัลกอริทึม O (N ^ 2) มีแนวโน้มที่จะทำงานช้าในทุกภาษา
Keith Thompson

คำตอบ:


169

สมมติว่าคำถามของคุณเกี่ยวข้องGNU grepโดยเฉพาะ นี่คือบันทึกจากผู้เขียน Mike Haertel:

GNU grep นั้นเร็วเพราะมันหลีกเลี่ยงการมองทุกไบต์ที่ป้อนข้อมูล

GNU grep เป็นไปอย่างรวดเร็วเพราะมันรันคำสั่งน้อยมากสำหรับแต่ละไบต์ว่ามัน ไม่ดูที่

GNU grep ใช้อัลกอริทึม Boyer-Moore ที่รู้จักกันดีซึ่งจะมองหาตัวอักษรสุดท้ายของสตริงเป้าหมายเป็นอันดับแรกและใช้ตารางการค้นหาเพื่อบอกว่ามันสามารถข้ามอินพุตไปได้ไกลแค่ไหนเมื่อใดก็ตามที่พบอักขระที่ไม่ตรงกัน

GNU grep ยังคลายการวนรอบภายในของ Boyer-Moore และตั้งค่ารายการตารางเดลต้า Boyer-Moore ในลักษณะที่ไม่จำเป็นต้องทำการทดสอบการออกจากลูปในทุกขั้นตอนที่ไม่มีการควบคุม ผลลัพธ์ของสิ่งนี้คือในขีด จำกัด GNU grep จะมีค่าเฉลี่ยน้อยกว่า 3 x86 คำสั่งที่ดำเนินการสำหรับแต่ละไบต์อินพุตที่ดูตามจริง (และข้ามหลายไบต์โดยสิ้นเชิง)

GNU grep ใช้การเรียกระบบอินพุต Unix แบบดิบและหลีกเลี่ยงการคัดลอกข้อมูลหลังจากอ่าน ยิ่งไปกว่านั้น GNU grep จะหลีกเลี่ยงการทำลายอินพุตเป็นเส้น การมองหา newlines จะทำให้ grep ช้าลงหลาย ๆ ครั้งเพราะการจะหา newlines นั้นจะต้องดูทุกไบต์!

ดังนั้นแทนที่จะใช้การป้อนข้อมูลเชิงเส้น GNU grep จะอ่านข้อมูลดิบลงในบัฟเฟอร์ขนาดใหญ่ค้นหาบัฟเฟอร์โดยใช้ Boyer-Moore และเมื่อพบการจับคู่เท่านั้นจึงจะไปและมองหาบรรทัดใหม่ที่มีขอบเขต (ตัวเลือกบรรทัดคำสั่งบางอย่างเช่น - n ปิดการใช้งานการเพิ่มประสิทธิภาพนี้)

คำตอบนี้เป็นส่วนหนึ่งของข้อมูลที่นำมาจากที่นี่


41

เพื่อเพิ่มคำตอบที่ยอดเยี่ยมของสตีฟ

อาจไม่เป็นที่รู้จักในวงกว้าง แต่ grep มักจะเร็วกว่าเมื่อgrep สำหรับสตริงรูปแบบที่ยาวกว่าแบบสั้นเพราะในรูปแบบที่ยาวกว่าBoyer-Mooreสามารถข้ามไปข้างหน้าได้ในระยะที่ยาวขึ้นเพื่อให้ได้ความเร็วในแนวเส้นใต้ที่ดียิ่งขึ้น:

ตัวอย่าง:

# after running these twice to ensure apples-to-apples comparison
# (everything is in the buffer cache) 

$ time grep -c 'tg=f_c' 20140910.log
28
0.168u 0.068s 0:00.26

$ time grep -c ' /cc/merchant.json tg=f_c' 20140910.log
28
0.100u 0.056s 0:00.17

แบบยาวเร็วขึ้น 35%!

มาได้ยังไง? Boyer-Mooreรวบรวมตารางการข้ามไปข้างหน้าจากสตริงรูปแบบและเมื่อใดก็ตามที่ไม่ตรงกันระบบจะเลือกการข้ามที่ยาวที่สุดเท่าที่จะเป็นไปได้ (จากอักขระสุดท้ายไปยังตัวแรก) ก่อนที่จะเปรียบเทียบอักขระเดียวในอินพุตกับอักขระในตารางข้าม

นี่คือวิดีโออธิบาย Boyer Moore (ให้เครดิตกับ kommradHomer)

ความเข้าใจผิดทั่วไปอีกประการหนึ่ง (สำหรับ GNU grep) fgrepคือเร็วกว่าgrepไฟล์. fin fgrepไม่ได้หมายถึง 'fast' ย่อมาจาก 'fixed' (ดู man page) และเนื่องจากทั้งสองเป็นโปรแกรมเดียวกันและทั้งคู่ใช้Boyer-Mooreจึงไม่มีความแตกต่างในความเร็วระหว่างการค้นหาคงที่ - สตริงที่ไม่มีอักขระพิเศษ regexp เพียงใช้ฉันเหตุผลfgrepคือเมื่อมีถ่าน regexp พิเศษ (เช่น., []หรือ*) ผมไม่อยากให้มันถูกตีความว่าเป็นเช่นนั้น และแม้แล้วแบบพกพามากขึ้นรูปแบบ / มาตรฐานเป็นที่ต้องการมากกว่าgrep -Ffgrep


3
เป็นเรื่องง่ายที่รูปแบบที่ยาวขึ้นจะเร็วขึ้น ถ้ารูปแบบเป็นหนึ่งไบต์ grep จะต้องตรวจสอบทุกไบต์ หากรูปแบบมีขนาด 4 ไบต์อาจทำให้ข้าม 4 ไบต์ได้ หากรูปแบบยาวเท่ากับข้อความ grep จะทำเพียงขั้นตอนเดียว
noel

12
ใช่มันเป็นเรื่องง่าย - ถ้าคุณเข้าใจวิธีการทำงานของ Boyer-Moore
arielf

2
อย่างอื่นก็ใช้งานง่าย การหาเข็มยาวในกองหญ้าจะง่ายกว่าเข็มสั้น
RajatJ

2
ตัวอย่างการโต้กลับสำหรับ "เร็วขึ้นเมื่อนานขึ้น" คือกรณีที่คุณต้องทำการทดสอบจำนวนมากก่อนที่จะล้มเหลวและคุณไม่สามารถก้าวไปข้างหน้าได้ บอกว่าไฟล์xs.txtมี 100000000 'x และคุณแล้วมันจริงล้มเหลวในการหาคู่เร็วกว่าถ้าคุณทำgrep yx xs.txt grep yxxxxxxxxxxxxxxxxxxx xs.txtการปรับปรุง Boyer-Moore-Horspool ให้กับ Boyer-Moore ช่วยปรับปรุงการข้ามไปข้างหน้าในกรณีนี้ แต่อาจจะไม่เป็นเพียงคำสั่งเครื่องสามคำสั่งในกรณีทั่วไป
lrn

2
@Tino ขอบคุณ ใช่ดูเหมือนว่าวันที่ (GNU) grep/fgrep/egrepจะเป็นฮาร์ดลิงก์ทั้งหมดไปยังไฟล์ปฏิบัติการเดียวกันจะหมดไป พวกเขา (และนามสกุลอื่น ๆ เช่นz*grep bz*greputils ซึ่งขยายในการบิน) grepอยู่ในขณะนี้ขนาดเล็กเปลือกห่อรอบ ความคิดเห็นทางประวัติศาสตร์ที่น่าสนใจบางประการเกี่ยวกับการสลับระหว่างการดำเนินการและการห่อเชลล์เดียวสามารถพบได้ในคอมมิตนี้: git.savannah.gnu.org/cgit/grep.git/commit/…
arielf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.