ต้องการบางสิ่งที่เร็วกว่า“ wc -l”


12

สำหรับไฟล์ขนาดใหญ่เช่น 1GB นั้นwc -lจะช้า เรามีวิธีคำนวณจำนวนบรรทัดใหม่สำหรับไฟล์ที่ต้องการได้เร็วขึ้นหรือไม่


25
ซื้อดิสก์ที่เร็วกว่านี้ไหม ระบุว่าแต่ละไบต์ของอินพุตจะต้องตรวจสอบความไม่มีประสิทธิภาพของ0x0AI / O จึงไม่ต้องสงสัยเลยว่าคอขวด
thrig

2
หากคุณสงสัยว่าของที่มีค่าใช้จ่ายมากเกินไปคุณอาจพยายามที่จะใช้ของคุณเองwc foreach byte in file: if byte == '\n': linecount++หากมีการใช้งานใน C หรือแอสเซมเบลอร์ฉันไม่คิดว่ามันจะเร็วขึ้นยกเว้นในพื้นที่เคอร์เนลใน RTOS ที่มีลำดับความสำคัญสูงสุด (หรือแม้แต่ใช้การขัดจังหวะสำหรับสิ่งนั้น - คุณไม่สามารถทำสิ่งใดกับระบบ .. เอาล่ะฉันพูดนอกเรื่อง ;-))
Murphy

3
และเพิ่งจะได้รับความรู้สึกสำหรับขนาดที่ผมทำอย่างรวดเร็วได้time wc -l some_movie.aviในไฟล์ uncached 5172672 some_movie.avi -- real 0m57.768s -- user 0m0.255s -- sys 0m0.863sผลใน ซึ่งโดยทั่วไปพิสูจน์แล้วว่า @thrig ถูกต้อง I / O จะตัดการแสดงของคุณในกรณีนี้
Murphy

10
วิธีที่ดีที่สุดในการแสดงว่าเป็นดิสก์ IO คอขวดทำtime wc -l some_large_file_smaller_than_cacheสองครั้งอย่างต่อเนื่องและดูว่าการดำเนินการที่สองนั้นรวดเร็วเพียงใดtime wc -l some_large_file_larger_than_cacheและดูว่าเวลาไม่เปลี่ยนแปลงระหว่างการทำงานอย่างไร สำหรับไฟล์ ~ 280MB ที่นี่เวลาเปลี่ยนจาก 1.7 วินาทีเป็น 0.2 วินาที แต่สำหรับไฟล์ 2GB นั่นคือ 14 วินาทีทั้งสองครั้ง
EightBitTony

1
ช้าเกินไปสำหรับคุณช้าแค่ไหน? สิ่งที่ไม่/usr/bin/time wc -l <file>พูดอะไรฮาร์ดแวร์ของคุณหรือไม่ มันเร็วขึ้นไหมถ้าคุณรันคำสั่งซ้ำ ๆ ? เราต้องการข้อมูลเพิ่มเติมจริง ๆ )
marcelm

คำตอบ:


21

คุณสามารถลองเขียนใน C:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(){
  char buf[BUFSIZ];
  int nread;
  size_t nfound=0;
  while((nread=read(0, buf, BUFSIZ))>0){
    char const* p;
    for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}
  }
  if(nread<0) { perror("Error"); return 1; }
  printf("%lu\n", nfound);
  return 0;
}

บันทึกใน, เช่น, wcl.cรวบรวมเช่นกับgcc wcl.c -O2 -o wclและทำงานด้วย

<yourFile ./wcl

สิ่งนี้พบว่ามีการขึ้นบรรทัดใหม่ในไฟล์ 1GB บนระบบของฉันในเวลาประมาณ370 มิลลิวินาที (ทำงานซ้ำ ๆ ) (การเพิ่มขนาดบัฟเฟอร์เพิ่มเวลาเพิ่มขึ้นเล็กน้อยซึ่งคาดว่าจะเกิดขึ้น - BUFSIZ ควรใกล้เคียงที่สุด) ก็เปรียบได้มากที่จะ ~ 380mswc -lฉันได้รับจาก

การวาดภาพทำให้ฉันมีเวลาที่ดีขึ้นประมาณ280 มิลลิวินาทีแต่แน่นอนว่ามีข้อ จำกัด ในการ จำกัด ไฟล์จริง (ไม่มี FIFOS ไม่มีอินพุตเทอร์มินัล ฯลฯ ):

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
  struct stat sbuf;
  if(fstat(0, &sbuf)<0){ perror("Can't stat stdin"); return 1; }

  char* buf = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, 0/*stdin*/, 0/*offset*/);
  if(buf == MAP_FAILED){ perror("Mmap error"); return 1; } 

  size_t nread = sbuf.st_size, nfound=0;
  char const* p;
  for(p=buf; p=memchr(p,'\n',nread-(p-buf)); nfound++,p++) {;}

  printf("%lu\n", nfound);
  return 0;
}

ฉันสร้างไฟล์ทดสอบด้วย:

 $ dd if=/dev/zero of=file bs=1M count=1042 

และเพิ่มการทดสอบบรรทัดใหม่ด้วย:

 $ echo >> 1GB 

และตัวแก้ไข hex


ฉันประหลาดใจที่ผลลัพธ์ mmap TBH ฉันเคยคิดว่าการสร้าง mmaping นั้นเร็วกว่าการอ่าน / เขียน แต่จากนั้นฉันก็เห็นการวัดประสิทธิภาพของ linux ที่ตรงกันข้าม ดูเหมือนว่ามันจะเป็นจริงในกรณีนี้
PSkocik

4
mmap กำลังจะได้รับผลลัพธ์ที่ดีขึ้นอย่างมากมายใน linux เพราะมันจะแมปไปยังหน้าเว็บขนาดใหญ่ในวันนี้และการพลาด TLB นั้นเป็นสิ่งที่พูดไม่ออก
jthill

อาจมีประโยชน์บางอย่างในการอ่านส่วนต่าง ๆ ของไฟล์ในเธรดแยกต่างหาก (เช่นด้วยforลูปOpenMP ) เพื่อให้ความคืบหน้าบางอย่างเกิดขึ้นในขณะที่เธรดเดียวรอจนอินพุต แต่ในทางกลับกันมันอาจขัดขวางการตั้งเวลา I / O ดังนั้นสิ่งที่ฉันสามารถแนะนำคือลองและวัด!
Toby Speight

read()รุ่นอาจได้รับประโยชน์จากการอ่านล่วงหน้า
Barmar

1
@TobySpeight ใช่การมัลติเธรดอาจทำให้เร็วขึ้น การดูการสแกนครั้งละสองไบต์ผ่านการค้นหาตาราง 2 ^ 16 ยังให้ความเร็วที่ค่อนข้างดีในการเล่นครั้งล่าสุด
PSkocik

18

คุณสามารถปรับปรุงในการแก้ปัญหาที่แนะนำโดย @pskocik readโดยการลดจำนวนสายไป มีจำนวนมากที่เรียกการอ่านBUFSIZchunks จากไฟล์ 1Gb วิธีปกติในการทำเช่นนี้คือการเพิ่มขนาดบัฟเฟอร์:

  • เพียงเพื่อความสนุกลองเพิ่มขนาดบัฟเฟอร์เป็น 10 หรือ 100 บนเดเบียน 7 ของฉันBUFSIZคือ 8192 ด้วยโปรแกรมดั้งเดิมนั่นคือการอ่าน 120,000 ครั้ง คุณอาจจะสามารถรับอินพุตบัฟเฟอร์ 1Mb เพื่อลดลงได้ 100%
  • สำหรับวิธีการที่เหมาะสมที่สุดแอพพลิเคชั่นอาจจัดสรรบัฟเฟอร์ให้ใหญ่พอ ๆ กับไฟล์ซึ่งต้องการการอ่านเพียงครั้งเดียว ซึ่งทำงานได้ดีพอสำหรับไฟล์ "เล็ก" (แม้ว่าผู้อ่านบางรายจะมีมากกว่า 1Gb บนเครื่อง)
  • ในที่สุดคุณสามารถทดสอบด้วย I / O ที่จับคู่หน่วยความจำซึ่งจัดการการจัดสรรเช่นนี้

เมื่อทำการเปรียบเทียบวิธีการต่าง ๆ คุณอาจทราบว่าบางระบบ (เช่น Linux) ใช้หน่วยความจำที่ไม่ได้ใช้ของเครื่องส่วนใหญ่เป็นดิสก์แคช เมื่อสักครู่ (เกือบ 20 ปีก่อนที่กล่าวถึงในคำถามที่พบบ่อยชั่วช้า ) ฉันรู้สึกงุนงงกับผลลัพธ์ที่ไม่คาดคิดจากอัลกอริทึมการเพจ (ไม่ดีมาก) ซึ่งฉันได้พัฒนาขึ้นเพื่อจัดการกับเงื่อนไขหน่วยความจำต่ำในโปรแกรมแก้ไขข้อความ ฉันอธิบายว่ามันวิ่งเร็วเพราะโปรแกรมทำงานจากบัฟเฟอร์หน่วยความจำที่ใช้ในการอ่านไฟล์และเฉพาะเมื่อไฟล์นั้นถูกอ่านซ้ำหรือเขียนจะมีความแตกต่างของความเร็ว

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

อ่านเพิ่มเติม:


2
คุณกำลังประเมินอิทธิพลของขนาดบัฟเฟอร์สูงกว่าเกณฑ์ที่กำหนด โดยทั่วไปแล้วการเพิ่มขนาดบัฟเฟอร์เกิน 4KB-ish ไม่ได้ช่วยอะไรมากและในความเป็นจริงอาจเป็นอันตรายเพราะมันอาจผลักบัฟเฟอร์ออกจากแคช L1 บนเครื่องของฉันการทดสอบddโดยใช้บัฟเฟอร์ขนาด 1MB นั้นช้ากว่า 8KB ค่าเริ่มต้นของ 8KB สำหรับ wc นั้นได้รับการคัดเลือกเป็นอย่างดีซึ่งจะใกล้เคียงกับช่วงที่เหมาะสมที่สุดสำหรับระบบขนาดใหญ่
marcelm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.