ทำไม wc จึงช้า


17

ทำไมยูทิลิตี wc จึงช้า?

เมื่อฉันเรียกใช้บนไฟล์ขนาดใหญ่จะใช้เวลานานกว่า md5sum ประมาณ 20 เท่า:

MyDesktop:/tmp$ dd if=/dev/zero bs=1024k count=1024 of=/tmp/bigfile
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.687094 s, 1.6 GB/s

MyDesktop:/tmp$ time wc /tmp/bigfile 
         0          0 1073741824 /tmp/bigfile

real    0m45.969s
user    0m45.424s
sys     0m0.424s

MyDesktop:/tmp$ time md5sum /tmp/bigfile 
cd573cfaace07e7949bc0c46028904ff  /tmp/bigfile

real    0m2.520s
user    0m2.196s
sys     0m0.316s

มันไม่ใช่แค่เงื่อนไขขอบแปลก ๆ ที่เกิดจากไฟล์ที่เต็มไปด้วยค่า Null แต่ฉันเห็นความแตกต่างของประสิทธิภาพแม้ว่าไฟล์จะเต็มไปด้วยข้อมูลแบบสุ่มหรือเป็นไฟล์ข้อความ

(นี่คือ Ubuntu 13.04, 64 บิต)


หมายเหตุสำหรับผู้ที่สนใจเฉพาะการนับบรรทัด: wc -l <filename> นั้นเร็วกว่ามากสำหรับไฟล์ที่มีขนาดใหญ่มาก
EL

คำตอบ:


27

ดังนั้นฉันไปที่แหล่งข้อมูลและดูเหมือนว่าความช้าในการจัดการอักขระไบต์คู่ โดยพื้นฐานแล้วสำหรับตัวละครทุกตัวที่อ่านนั้นจำเป็นต้องเรียกmbrtowc()ให้พยายามแปลงเป็นอักขระตัวกว้างจากนั้นอักขระตัวกว้างจะถูกทดสอบเพื่อดูว่าเป็นตัวคั่นคำตัวแยกบรรทัด ฯลฯ

แน่นอนถ้าฉันเปลี่ยนLANGตัวแปรโลแคลของฉันจากค่าเริ่มต้นen_US.UTF-8(UTF-8 เป็นชุดอักขระหลายไบต์) และตั้งเป็น " C" (ชุดอักขระไบต์เดี่ยวแบบง่าย) wcสามารถใช้การปรับให้เหมาะสมแบบไบต์เดียวซึ่งเพิ่มความเร็วได้มาก ใช้เวลาประมาณหนึ่งในสี่นานเท่าที่เคยมีมา

นอกจากนี้จะต้องตรวจสอบตัวละครแต่ละตัวหากนับจำนวนคำ ( -w) ความยาวบรรทัด ( -L) หรือตัวอักษร ( -m) ถ้ามันเป็นเพียงการทำไบต์และ / หรือการนับเส้นก็สามารถข้ามการจัดการตัวกว้างและจากนั้นมันจะทำงานได้อย่างรวดเร็วมาก - md5sumเร็วกว่า

ฉันวิ่งมันผ่านgprofและฟังก์ชั่นที่ใช้ในการจัดการตัวอักษรสัญลักษณ์นี้ ( mymbsinit(), mymbrtowc(), myiswprint()ฯลฯ ) มีการขึ้นประมาณ 30% ของเวลาดำเนินการเพียงอย่างเดียวและรหัสที่ขั้นตอนผ่านบัฟเฟอร์ที่ซับซ้อนมากขึ้นเพราะมีการ จัดการขั้นตอนขนาดผันแปรผ่านบัฟเฟอร์สำหรับอักขระขนาดผันแปรตลอดจนการบรรจุอักขระที่เสร็จสมบูรณ์บางส่วนที่ขยายบัฟเฟอร์กลับไปที่จุดเริ่มต้นของบัฟเฟอร์เพื่อให้สามารถจัดการได้ในครั้งถัดไป

ตอนนี้ฉันรู้ว่าควรมองหาอะไรฉันพบโพสต์สองสามข้อความที่พูดถึง utf-8 slowness ด้วยยูทิลิตี้บางตัว:

/programming/13913014/grepping-a-huge-file-80gb-any-way-to-speed-it-up http://dtrace.org/blogs/brendan/2011/12/08 / 2000x ประสิทธิภาพชนะ /


2
โอ้เพิ่งรู้ว่าคุณเป็น OP : p
Ivan Chau

2
แม้ว่านี่จะเป็นคำตอบที่ถูกโหวตมากที่สุด แต่ก็ไม่เกี่ยวข้อง md5sumจะไม่อนุญาตให้คุณนับจำนวนคำและwcจะไม่คำนวณ md5 hash ของไฟล์! มันเหมือนกับถามว่าทำไมรถของฉันถึงช้านักเมื่อเทียบกับเครื่องพิมพ์ดีดเมื่อเขียนข้อความ
user49468

5
@ user49468: มีเหตุผลที่จะสมมติว่าทั้งสองเป็น IO-bound เนื่องจากทั้งสองต้องอ่านแต่ละไบต์ของไฟล์อินพุต คำตอบนี้พิสูจน์ว่าwcจริงๆแล้วเป็น CPU-bound เมื่อประมวลผลอักขระหลายไบต์
MSalters

2
@ user49468: wc และ md5sum อาจทำสิ่งที่แตกต่างกัน แต่ทั้งคู่อ่านไฟล์และทำการคำนวณอย่างง่าย ๆ หนึ่งคำนวณการตรวจสอบหนึ่งนับไบต์ไบต์แยกคำและขึ้นบรรทัดใหม่ ฉันคิดว่ามันง่าย แต่ไม่ได้รวมอยู่ในความซับซ้อนของชุดอักขระหลายไบต์ มันเหมือนถามว่า "ทำไมรถของฉันถึงเร็วกว่าการไปร้านถึง 20 เท่า?" คุณคาดหวังความแตกต่างระหว่างสองอย่างนี้ แต่ไม่ใช่ความแตกต่าง 20X
จอห์นนี่

1
@Johnny you car / minivan การเปรียบเทียบไม่มีมุมมองที่ทั้งคู่ออกแบบมาเพื่อการขนส่งคุณไปที่ร้าน ดังนั้นการเปรียบเทียบความเร็วอยู่ในสถานที่ การเปรียบเทียบรถของคุณกับรถยนต์ที่มีลายทางนั้นเหมาะสมกว่า เพียงเพราะทั้งสองใช้ถนนความเร็วของพวกเขาไม่เกี่ยวข้องเนื่องจากจิตรกรลายไม่เหมาะที่จะไปช้อปปิ้งและในทางกลับกัน
user49468

1

แค่เดา ​​แต่คุณเป็นประเภทเปรียบเทียบแอปเปิ้ลกับส้มด้วยความเคารพต่อสิ่งที่wcทำกับสิ่งที่md5sumกำลังทำ

งานของ md5sum

เมื่อmd5sumประมวลผลไฟล์มันจะเปิดไฟล์เป็นสตรีมแล้วเริ่มเรียกใช้สตรีมผ่านฟังก์ชันการตรวจสอบ MD5ซึ่งต้องการหน่วยความจำน้อยมาก โดยพื้นฐานแล้ว CPU & disk I / O ถูกผูกไว้

งานของ wc

เมื่อwcเรียกใช้มันทำงานได้มากขึ้นแล้วเพียงแยกวิเคราะห์ไฟล์อักขระในแต่ละครั้ง มันจะต้องวิเคราะห์โครงสร้างของไฟล์บรรทัดในแต่ละครั้งเพื่อกำหนดว่าขอบเขตระหว่างตัวละครอยู่ที่ใดและเป็นขอบเขตของคำหรือไม่

ตัวอย่าง

ลองนึกถึงสตริงต่อไปนี้และวิธีที่อัลกอริธึมแต่ละอันจะต้องเคลื่อนผ่านมันอย่างไร

“Hello! Greg”
“Hello!Greg”
“Hello\nGreg”
“A.D.D.”
“Wow, how great!”
“wow     \n\n\n    great”
“it was a man-eating shark.”

สำหรับ MD5 นั้นจะเคลื่อนผ่านอักขระเหล่านี้ทีละตัว เพราะwcจะต้องตัดสินใจว่าอะไรคือขอบเขตของคำและบรรทัดและติดตามจำนวนการเกิดขึ้นที่เห็น

การสนทนา wc เพิ่มเติม

ฉันพบความท้าทายในการเขียนโค้ดจากปี 2549ที่กล่าวถึงการนำไปใช้wcใน. NET ความยากลำบากนั้นค่อนข้างชัดเจนเมื่อคุณดูรหัสหลอกบางอย่างดังนั้นสิ่งนี้อาจช่วยให้คุณเริ่มเข้าใจถึงสาเหตุwcที่ทำให้ช้ากว่าการดำเนินการอื่น ๆ


1
คุณกำลังอธิบายบางสิ่งที่แตกต่างจากคำสั่งUnix wc (อย่างน้อยไม่ใช่คำสั่งที่มาพร้อมกับ Ubuntu) wc นั้นไม่นับคำที่ไม่ซ้ำกันเพียงแค่คำว่า "สวัสดีชาวโลก" คือ 3 คำไม่ใช่ 2
Johnny

จากทฤษฎีนี้ดูเหมือนว่างานที่ง่ายกว่าเช่นการนับบรรทัดจะทำงานได้เร็วขึ้น การเปลี่ยน 'wc' เพื่อระบุจำนวนบรรทัดแก้ไขผลลัพธ์อย่างมีนัยสำคัญหรือไม่? 'wc -l'
โจชัวมิลเลอร์

@Johnny - ฉันไม่เคยพูดว่ามันนับคำที่ไม่ซ้ำกันที่คุณพูดว่า wcนับหลายสิ่งเมื่อแยกวิเคราะห์ไฟล์ มันนับจำนวนคำบรรทัดและไบต์ตามที่แยกวิเคราะห์ไฟล์ อ่าน man page!
slm

@JoshuaMiller - ไม่ชัดเจนว่าจะบอกwcให้นับเฉพาะการ จำกัด บรรทัดหรือไม่การแยกวิเคราะห์ภายในเพื่อที่จะนับเฉพาะสิ่งเหล่านี้หรือเพียงแค่รายงานผลลัพธ์ในบรรทัดแม้ว่าจะยังนับทุกอย่างอยู่ก็ตาม
slm

@slm คุณพูดว่ามันนับคำที่ไม่ซ้ำตัวอย่างของคุณพูดว่า "Hello! Greg” ให้ผลลัพธ์ใน Hello 1, Greg 1 , เช่นนั้นจะนับสำหรับแต่ละคำ และโครงการ. Net ที่คุณเชื่อมโยงเพื่อพูดว่า "หนึ่งในภารกิจหลักของมันคือการผ่านชุดข้อมูลและนับจำนวนการทำซ้ำของคำที่กำหนดตัวอย่างเช่นการกำหนดประโยค“ Hello, yes hello” มันจะบอกคุณว่า คำว่าสวัสดีถูกใช้สองครั้งและคำว่าใช่ถูกใช้เพียงครั้งเดียว " ในขณะที่ในความเป็นจริงผลลัพธ์ของecho "สวัสดีใช่สวัสดี" | ห้องสุขา - คำว่า "3" ไม่ใช่ "Hello: 2, ใช่: 1"
Johnny
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.