ยูทิลิตี ugrep-indexer จัดทำดัชนีไฟล์แบบเรียกซ้ำเพื่อเพิ่มความเร็วในการ grepping แบบเรียกซ้ำ
นอกจากนี้เนื้อหาของไฟล์เก็บถาวรและไฟล์บีบอัดจะถูกจัดทำดัชนีเมื่อระบุด้วยตัวเลือกบรรทัดคำสั่ง วิธีนี้จะกำจัดการค้นหาเมื่อไม่มีเนื้อหาใดที่ตรงกับรูปแบบที่ระบุ
ugrep เป็นตัวค้นหาไฟล์ที่รวดเร็วซึ่งเข้ากันได้กับ grep ซึ่งรองรับการค้นหาตามดัชนี การค้นหาตามดัชนีสามารถทำได้เร็วขึ้นอย่างมากในระบบไฟล์ที่ช้า และเมื่อการแคชระบบไฟล์ไม่ได้ผล: หากระบบไฟล์บนไดรฟ์ที่ค้นหาไม่ได้ถูกแคชไว้ใน RAM กล่าวคือ เป็น "เย็น" การทำดัชนีจะทำให้การค้นหาเร็วขึ้น ค้นหาเฉพาะไฟล์ที่อาจตรงกับรูปแบบ regex ที่ระบุโดยใช้ดัชนีของไฟล์ ดัชนีนี้ช่วยให้ตรวจสอบได้อย่างรวดเร็วว่ามีรายการที่ตรงกันหรือไม่ ดังนั้นเราจึงหลีกเลี่ยงการค้นหาไฟล์ทั้งหมด
การค้นหาตามดัชนีด้วย ugrep นั้นปลอดภัยและจะไม่ข้ามไฟล์ที่อัปเดตซึ่งอาจตรงกันในขณะนี้ หากมีการเพิ่มหรือเปลี่ยนแปลงไฟล์และไดเร็กทอรีหลังจากการจัดทำดัชนี การค้นหาจะค้นหาการเพิ่มและการเปลี่ยนแปลงที่เกิดขึ้นกับระบบไฟล์เสมอโดยการเปรียบเทียบการประทับเวลาของไฟล์และไดเร็กทอรีกับการประทับเวลาการทำดัชนี
เมื่อมีการเพิ่มหรือเปลี่ยนแปลงไฟล์จำนวนมากหลังจากการจัดทำดัชนี เราอาจต้องการจัดทำดัชนีใหม่เพื่อให้ดัชนีเป็นปัจจุบัน การจัดทำดัชนีซ้ำจะเพิ่มขึ้น ดังนั้นจึงใช้เวลาไม่นานเท่ากับกระบวนการจัดทำดัชนีเริ่มแรก
ตัวอย่างทั่วไปแต่เล็กๆ ของการค้นหาตามดัชนี เช่น บนที่เก็บ ugrep v3.12.6 ที่วางอยู่บนไดรฟ์แยกต่างหาก:
$ cd drive/ugrep
$ ugrep-indexer -I
12247077 bytes scanned and indexed with 19% noise on average
1317 files indexed in 28 directories
28 new directories indexed
1317 new files indexed
0 modified files indexed
0 deleted files removed from indexes
128 binary files ignored with --ignore-binary
0 symbolic links skipped
0 devices skipped
5605227 bytes indexing storage increase at 4256 bytes/file
การค้นหาปกติบนระบบไฟล์แบบ Cold ที่ไม่มีการจัดทำดัชนีจะใช้เวลา 1.02 วินาทีหลังจากยกเลิกการต่อเชื่อม drive
และติดตั้งอีกครั้งเพื่อล้างแคช FS เพื่อบันทึกผลของการทำดัชนี:
$ ugrep -I -l 'std::chrono' --stats
src/ugrep.cpp
Searched 1317 files in 28 directories in 1.02 seconds with 8 threads: 1 matching (0.07593%)
Ripgrep 13.0.0 ใช้เวลานานกว่า 1.18 วินาทีสำหรับการค้นหาแบบเย็นเดียวกัน (ripgrep ข้ามไฟล์ไบนารี่ตามค่าเริ่มต้น ดังนั้นตัวเลือก -I
ไม่ได้ระบุ):
$ time rg -l 'std::chrono'
src/ugrep.cpp
1.18 real 0.01 user 0.06 sys
ในทางตรงกันข้าม เมื่อใช้การทำดัชนี การค้นหาระบบไฟล์แบบ cold จะใช้เวลาเพียง 0.0487 วินาทีด้วย ugrep ซึ่งเร็วกว่า 21 เท่า หลังจากยกเลิกการต่อเชื่อม drive
และติดตั้งอีกครั้งเพื่อล้างแคช FS เพื่อบันทึกผลของการทำดัชนี:
$ ugrep --index -I -l 'std::chrono' --stats
src/ugrep.cpp
Searched 1317 files in 28 directories in 0.0487 seconds with 8 threads: 1 matching (0.07593%)
Skipped 1316 of 1317 files with non-matching indexes
เวลาที่ผ่านไปมักจะมีความแปรปรวนอยู่เสมอ โดยที่ 0.0487 วินาทีเป็นเวลาที่ดีที่สุดในการค้นหาสี่ครั้ง ซึ่งสร้างช่วงเวลาการค้นหาที่ 0.0487 (เร็วขึ้น 21 เท่า) ถึง 0.0983 วินาที (เร็วขึ้น 10 เท่า)
ความเร็วที่เพิ่มขึ้นอาจสูงขึ้นอย่างมากโดยทั่วไปเมื่อเทียบกับการสาธิตขนาดเล็กนี้ ขึ้นอยู่กับปัจจัยหลายประการ ขนาดของไฟล์ที่จัดทำดัชนี ความเร็วในการอ่านของระบบไฟล์ และสมมติว่าไฟล์ส่วนใหญ่เป็นไฟล์เย็น
อัลกอริธึมการจัดทำดัชนีที่ฉันออกแบบนั้นมี ความซ้ำซากจำเจที่พิสูจน์ได้ : ความแม่นยำที่สูงขึ้นรับประกันประสิทธิภาพการค้นหาที่เพิ่มขึ้นโดยการลดอัตราผลบวกลวง แต่ยังเพิ่มค่าใช้จ่ายในการจัดเก็บดัชนีด้วย ในทำนองเดียวกัน ความแม่นยำที่ต่ำกว่าจะลดประสิทธิภาพการค้นหา แต่ยังลดค่าใช้จ่ายในการจัดเก็บดัชนีด้วย ดังนั้นฉันจึงตั้งชื่อตัวสร้างดัชนีของฉันว่า ตัวสร้างดัชนีแบบ monotonic
หากพื้นที่จัดเก็บไฟล์อยู่ในระดับพรีเมี่ยม เราสามารถลดค่าใช้จ่ายการจัดเก็บดัชนีลงได้โดยการระบุความแม่นยำในการจัดทำดัชนีที่ต่ำกว่า
การทำดัชนีตัวอย่างจากด้านบนด้วยระดับ 0 (ตัวเลือก -0
) จะช่วยลดค่าใช้จ่ายในการจัดเก็บการทำดัชนีลง 8.6 เท่า จาก 4256 ไบต์ต่อไฟล์เป็น 490 ไบต์ที่เลวร้ายต่อไฟล์:
12247077 bytes scanned and indexed with 42% noise on average
1317 files indexed in 28 directories
0 new directories indexed
1317 new files indexed
0 modified files indexed
0 deleted files removed from indexes
128 binary files ignored with --ignore-binary
0 symbolic links skipped
0 devices skipped
646123 bytes indexing storage increase at 490 bytes/file
การค้นหาที่จัดทำดัชนียังคงเร็วกว่าที่ไม่จัดทำดัชนีถึง 12 เท่าสำหรับตัวอย่างนี้ โดยมีการค้นหาไฟล์ 16 ไฟล์จริง (ผลบวกลวง 15 รายการ):
Searched 1317 files in 28 directories in 0.0722 seconds with 8 threads: 1 matching (0.07593%)
Skipped 1301 of 1317 files with non-matching indexes
รูปแบบ Regex ที่ซับซ้อนกว่าตัวอย่างนี้อาจมีอัตราผลบวกลวงที่สูงกว่าตามธรรมชาติ ซึ่งเป็นอัตราของไฟล์ที่ถือว่าอาจเข้าคู่กันเมื่อไม่เป็นเช่นนั้น อัตราผลบวกลวงที่สูงกว่าอาจลดความเร็วในการค้นหาเมื่อมีอัตราสูงพอที่จะส่งผลกระทบ
ตารางต่อไปนี้แสดงให้เห็นว่าความแม่นยำในการจัดทำดัชนีส่งผลต่อพื้นที่จัดเก็บการทำดัชนีและเสียงรบกวนโดยเฉลี่ยต่อไฟล์ที่ทำดัชนีอย่างไร คอลัมน์ขวาสุดแสดงความเร็วในการค้นหาและอัตราผลบวกลวงสำหรับ ugrep --index -I -l 'std::chrono'
:
ตามมาตรฐาน | การจัดเก็บดัชนี (KB) | เสียงเฉลี่ย | ผลบวกลวง | เวลาในการค้นหา |
---|---|---|---|---|
-0 | 631 | 42% | 15 | 0.0722 |
-1 | 1276 | 39% | 1 | 0.0506 |
-2 | 1576 | 36% | 0 | 0.0487 |
-3 | 2692 | 31% | 0 | ไม่เลย |
-4 | 2966 | 28% | 0 | ไม่เลย |
-5 | 4953 | 23% | 0 | ไม่เลย |
-6 | 5474 | 19% | 0 | ไม่เลย |
-7 | 9513 | 15% | 0 | ไม่เลย |
-8 | 10889 | 11% | 0 | ไม่เลย |
-9 | 13388 | 7% | 0 | ไม่เลย |
หาก regex ที่ระบุตรงกับรูปแบบที่เป็นไปได้อื่นๆ อีกมากมาย เช่น กับการค้นหา ugrep --index -I -l '(todo|TODO)[: ]'
เราอาจสังเกตเห็นอัตราการบวกลวงที่สูงกว่าในไฟล์ 1317 ที่ค้นหา ส่งผลให้ใช้เวลาค้นหานานขึ้นเล็กน้อย:
ตามมาตรฐาน | ผลบวกลวง | เวลาในการค้นหา |
---|---|---|
-0 | 189 | 0.292 |
-1 | 69 | 0.122 |
-2 | 43 | 0.103 |
-3 | 19 | 0.101 |
-4 | 16 | 0.097 |
-5 | 2 | 0.096 |
-6 | 1 | ไม่เลย |
-7 | 0 | ไม่เลย |
-8 | 0 | ไม่เลย |
-9 | 0 | ไม่เลย |
ความแม่นยำ -4
เป็นค่าเริ่มต้น (จาก -5
ก่อนหน้านี้ในรุ่นเก่า) ซึ่งมีแนวโน้มที่จะทำงานได้ดีมากในการค้นหาด้วยรูปแบบ regex ที่มีความซับซ้อนเล็กน้อย
คำเตือนหนึ่งคำ มีค่าใช้จ่ายเล็กน้อยในการตรวจสอบดัชนีเสมอ ซึ่งหมายความว่าหากไฟล์ทั้งหมดถูกแคชไว้ใน RAM แล้ว เนื่องจากไฟล์ถูกค้นหาหรืออ่านเมื่อเร็วๆ นี้ การจัดทำดัชนีจะไม่ทำให้การค้นหาเร็วขึ้นโดยไม่จำเป็น ในกรณีนั้น การค้นหาที่ไม่จัดทำดัชนีอาจเร็วกว่า นอกจากนี้ การค้นหาตามดัชนียังมีเวลาเริ่มต้นที่นานขึ้น เวลาเริ่มต้นนี้จะเพิ่มขึ้นเมื่อมีการใช้คลาสอักขระ Unicode และไวด์การ์ดที่ต้องแปลงเป็นตารางแฮช
โดยสรุป การค้นหาตามดัชนีจะมีประสิทธิภาพสูงสุดเมื่อค้นหาไฟล์ Cold จำนวนมาก และเมื่อรูปแบบ regex ไม่ตรงกันมากเกินไป เช่น เราต้องการจำกัดการใช้การทำซ้ำไม่จำกัด *
และ +
และจำกัดการใช้คลาสอักขระ Unicode เมื่อ เป็นไปได้. ซึ่งจะช่วยลดเวลาเริ่มต้น ugrep และจำกัดอัตราของการจับคู่รูปแบบผลบวกลวง (ดูคำถาม & คำตอบด้านล่าง)
จัดทำดัชนีไฟล์ที่ไม่ใช่ไบนารีซ้ำ ๆ และเพิ่มขึ้นทั้งหมดที่แสดงความคืบหน้า:
ugrep-indexer -I -v
จัดทำดัชนีไฟล์ที่ไม่ใช่ไบนารี่ซ้ำและแบบเพิ่มทีละไฟล์ รวมถึงไฟล์ที่ไม่ใช่ไบนารี่ที่จัดเก็บไว้ในไฟล์เก็บถาวรและในไฟล์บีบอัด แสดงความคืบหน้า:
ugrep-indexer -z -I -v
สร้างดัชนีไฟล์ที่ไม่ใช่ไบนารี่ทั้งหมดเพิ่มขึ้น รวมถึงไฟล์เก็บถาวรและไฟล์บีบอัด แสดงความคืบหน้า ติดตามลิงก์สัญลักษณ์ไปยังไฟล์ (แต่ไม่ใช่ไดเร็กทอรี) แต่อย่าสร้างดัชนีไฟล์และไดเร็กทอรีที่ตรงกับ globs ใน .gitignore:
ugrep-indexer -z -I -v -S -X
บังคับให้สร้างดัชนีใหม่ของไฟล์ที่ไม่ใช่ไบนารี่ทั้งหมด รวมถึงไฟล์เก็บถาวรและไฟล์บีบอัด ตามลิงก์สัญลักษณ์ไปยังไฟล์ (แต่ไม่ใช่ไปยังไดเร็กทอรี) แต่อย่าสร้างดัชนีไฟล์และไดเร็กทอรีที่ตรงกับ globs ใน .gitignore:
ugrep-indexer -f -z -I -v -S -X
เหมือนกัน แต่ลดการจัดเก็บไฟล์ดัชนีให้เหลือน้อยที่สุดโดยลดความแม่นยำในการจัดทำดัชนีจาก 5 (ค่าเริ่มต้น) เป็น 0:
ugrep-indexer -f -0 -z -I -v -S -X
เพิ่มประสิทธิภาพการค้นหาโดยเพิ่มความแม่นยำในการจัดทำดัชนีจาก 5 (ค่าเริ่มต้น) เป็น 7 โดยมีค่าใช้จ่ายเท่ากับไฟล์ดัชนีขนาดใหญ่:
ugrep-indexer -f7zIvSX
ลบไฟล์ดัชนี ._UG#_Store
ที่ซ่อนอยู่ทั้งหมดซ้ำๆ เพื่อคืนค่าแผนผังไดเร็กทอรีเป็นแบบไม่จัดทำดัชนี:
ugrep-indexer -d
กำหนดค่าและคอมไพล์ด้วย:
./build.sh
หากต้องการแต่ไม่จำเป็น ให้ติดตั้งด้วย:
sudo make install
เพิ่มตัวเลือกในการสร้างไฟล์ดัชนีหนึ่งไฟล์ เช่น ระบุไว้อย่างชัดเจนถึง ugrep วิธีนี้สามารถปรับปรุงความเร็วในการค้นหาที่จัดทำดัชนีเพิ่มเติมได้หากไฟล์ดัชนีอยู่ในระบบไฟล์ที่รวดเร็ว มิฉะนั้น อย่าคาดหวังการปรับปรุงมากนักหรืออาจช้าลงได้ เนื่องจากไม่สามารถค้นหาไฟล์ดัชนีเดียวพร้อมกันได้ และรายการดัชนีเพิ่มเติมจะถูกตรวจสอบเมื่อในความเป็นจริง ไดเร็กทอรีถูกข้าม (ข้ามดัชนีด้วย) การทดลองจะบอก ข้อแม้ที่สำคัญของแนวทางนี้คือการค้นหาตามดัชนีด้วย ugrep --index
นั้นไม่ปลอดภัยอีกต่อไป: ไฟล์ใหม่และไฟล์ที่แก้ไขที่ยังไม่ได้จัดทำดัชนีจะยังไม่ถูกค้นหา
ตัวกรอง N-gram Bloom แต่ละตัวมี "ระดับบิต" ของตัวเองในตารางแฮชเพื่อหลีกเลี่ยงความขัดแย้งของแฮช ตัวอย่างเช่น 2 กรัมไม่แชร์บิตใดๆ กับ 3 กรัม เพื่อให้แน่ใจว่าเราจะไม่มีผลบวกลวงใดๆ กับอักขระที่จับคู่อย่างไม่ถูกต้องซึ่งจริงๆ แล้วไม่ได้เป็นส่วนหนึ่งของรูปแบบ อย่างไรก็ตาม พื้นที่บิต 1 กรัม (อักขระเดี่ยว) มีขนาดเล็ก (สูงสุด 256 บิต) ดังนั้นเราจึงเสียบิตไปเมื่อตารางแฮชมีขนาดใหญ่ขึ้น วิธีที่เป็นไปได้ในการลดของเสียคือการรวม 1 กรัมกับ 2 กรัมเพื่อใช้พื้นที่บิตเดียวกัน วิธีนี้ทำได้ง่ายหากเราพิจารณาว่า 1 กรัมเท่ากับ 2 กรัมโดยตั้งค่าอักขระตัวที่สองเป็น