ฉันมีสถานการณ์ที่ไม่ซ้ำกันซึ่งฉันสามารถเปรียบเทียบโซลูชันที่เสนอในหน้านี้และดังนั้นฉันจึงเขียนคำตอบนี้เป็นการรวมของโซลูชันที่เสนอพร้อมเวลารันรวมสำหรับแต่ละข้อ
ติดตั้ง
ฉันมีไฟล์ข้อมูลข้อความ ASCII 3.261 กิกะไบต์ด้วยหนึ่งคู่ค่าคีย์ต่อแถว ไฟล์มีจำนวนทั้งหมด 3,339,550,320 แถวและเปิดอย่างหวุดหวิดในเครื่องมือแก้ไขใด ๆ ที่ฉันได้ลองรวมถึง Go-to Vim ของฉัน ฉันต้องเซ็ตย่อยไฟล์นี้เพื่อตรวจสอบค่าบางอย่างที่ฉันค้นพบเริ่มต้นรอบแถวเท่านั้น ~ 500,000,000
เนื่องจากไฟล์มีหลายแถว:
- ฉันต้องการแยกชุดย่อยของแถวเพื่อทำสิ่งที่มีประโยชน์กับข้อมูล
- การอ่านผ่านทุกแถวที่นำไปสู่คุณค่าที่ฉันสนใจจะใช้เวลานาน
- หากวิธีการอ่านผ่านแถวที่ฉันสนใจและอ่านต่อไปส่วนที่เหลือของไฟล์มันจะเสียเวลาในการอ่านเกือบ 3 พันล้านแถวที่ไม่เกี่ยวข้องและใช้เวลา 6x ยาวเกินความจำเป็น
กรณีที่ดีที่สุดของฉันคือโซลูชันที่แยกเพียงหนึ่งบรรทัดจากไฟล์โดยไม่อ่านแถวอื่น ๆ ในไฟล์ แต่ฉันคิดไม่ออกเลยว่าจะทำสิ่งนี้อย่างไรใน Bash
สำหรับจุดประสงค์ของการมีสติของฉันฉันจะไม่พยายามอ่านเต็ม 500,000,000 บรรทัดที่ฉันต้องการสำหรับปัญหาของตัวเอง แต่ฉันจะพยายามแยกแถว 50,000,000 ออกจาก 3,339,550,320 (ซึ่งหมายความว่าการอ่านไฟล์เต็มจะใช้เวลานานกว่าที่จำเป็น 60x)
ฉันจะใช้time
บิวด์อินเพื่อเปรียบเทียบมาตรฐานแต่ละคำสั่ง
พื้นฐาน
ก่อนอื่นเรามาดูวิธีการhead
tail
แก้ปัญหา:
$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0
real 1m15.321s
ค่าพื้นฐานสำหรับแถวที่ 50 ล้านคือ 00: 01: 15.321 ถ้าฉันไปตรงแถวที่ 500 ล้านมันอาจจะเป็น ~ 12.5 นาที
ตัด
ฉันสงสัยเรื่องนี้ แต่มันก็คุ้มค่ากับการยิง:
$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0
real 5m12.156s
อันนี้ใช้เวลา 00: 05: 12.156 ในการวิ่งซึ่งช้ากว่าพื้นฐานมาก! ฉันไม่แน่ใจว่าจะอ่านไฟล์ทั้งไฟล์หรือสูงถึง 50 ล้านบรรทัดก่อนที่จะหยุดทำงาน แต่ไม่ว่าจะเป็นการแก้ปัญหาที่ทำงานได้จริงหรือไม่
AWK
ฉันรันโซลูชันด้วยexit
เพราะฉันจะไม่รอให้ไฟล์เต็มรูปแบบทำงาน:
$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0
real 1m16.583s
รหัสนี้ทำงานใน 00: 01: 16.583 ซึ่งช้ากว่า ~ 1 วินาทีเท่านั้น แต่ก็ยังไม่พัฒนาในระดับพื้นฐาน ในอัตรานี้หากคำสั่ง exit ถูกแยกออกมันอาจใช้เวลาประมาณ 76 นาทีในการอ่านไฟล์ทั้งหมด!
Perl
ฉันใช้โซลูชัน Perl ที่มีอยู่เช่นกัน:
$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0
real 1m13.146s
รหัสนี้วิ่งใน 00: 01: 13.146 ซึ่งเร็วกว่า baseline ประมาณ 2 วินาที หากฉันใช้เต็ม 500,000,000 อาจใช้เวลาประมาณ 12 นาที
sed
คำตอบที่ดีที่สุดบนกระดานนี่คือผลลัพธ์ของฉัน:
$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0
real 1m12.705s
รหัสนี้ทำงานใน 00: 01: 12.705 ซึ่งเร็วกว่า baseline 3 วินาทีและเร็วกว่า Perl ~ 0.4 วินาที ถ้าฉันวิ่งเต็ม 500,000,000 แถวมันคงจะใช้เวลาประมาณ 12 นาที
mapfile
ฉันทุบตี 3.1 และดังนั้นจึงไม่สามารถทดสอบวิธีแก้ปัญหา mapfile
ข้อสรุป
ดูเหมือนว่าส่วนใหญ่เป็นการยากที่จะปรับปรุงhead
tail
วิธีการแก้ปัญหา sed
ทางออกที่ดีที่สุดให้เพิ่มขึ้น ~ 3% ในประสิทธิภาพ
(เปอร์เซ็นต์ที่คำนวณด้วยสูตร% = (runtime/baseline - 1) * 100
)
แถว 50,000,000
- 00: 01: 12.705 (-00: 00: 02.616 = -3.47%)
sed
- 00: 01: 13.146 (-00: 00: 02.175 = -2.89%)
perl
- 00: 01: 15.321 (+00: 00: 00.000 = + 0.00%)
head|tail
- 00: 01: 16.583 (+00: 00: 01.262 = + 1.68%)
awk
- 00: 05: 12.156 (+00: 03: 56.835 = + 314.43%)
cut
แถว 500,000,000
- 00: 12: 07.050 (-00: 00: 26.160)
sed
- 00: 12: 11.460 (-00: 00: 21.750)
perl
- 00: 12: 33.210 (+00: 00: 00.000)
head|tail
- 00: 12: 45.830 (+00: 00: 12.620)
awk
- 00: 52: 01.560 (+00: 40: 31.650)
cut
แถว 3,338,559,320
- 01: 20: 54.599 (-00: 03: 05.327)
sed
- 01: 21: 24.045 (-00: 02: 25.227)
perl
- 01: 23: 49.273 (+00: 00: 00.000)
head|tail
- 01: 25: 13.548 (+00: 02: 35.735)
awk
- 05: 47: 23.026 (+04: 24: 26.246)
cut
awk
และsed
และฉันแน่ใจว่าบางคนสามารถเกิดขึ้นกับ Perl หนึ่งซับหรือดังนั้น;)