วิธีที่เร็วและมีประสิทธิภาพที่สุดในการรับจำนวนเรคคอร์ด (บรรทัด) ในไฟล์บีบอัด gzip


16

ฉันพยายามที่จะทำบันทึกนับในไฟล์ 7.6 GB gzip ฉันพบวิธีการเล็กน้อยโดยใช้zcatคำสั่ง

$ zcat T.csv.gz | wc -l
423668947

วิธีนี้ใช้งานได้ แต่ใช้เวลานานเกินไป (มากกว่า 10 นาทีในการนับ) ฉันลองวิธีอื่นอีกสองสามอย่างเช่น

$ sed -n '$=' T.csv.gz
28173811
$ perl -lne 'END { print $. }' < T.csv.gz
28173811
$ awk 'END {print NR}' T.csv.gz
28173811

ทั้งสามคำสั่งเหล่านี้กำลังดำเนินการอย่างรวดเร็ว แต่ให้นับไม่ถูกต้องของ 28173811

ฉันจะนับจำนวนเร็กคอร์ดในเวลาน้อยที่สุดได้อย่างไร


5
ทำไมคุณต้องนับจำนวนบันทึก? หากคุณพยายามนับพวกเขาก่อนที่จะประมวลผลนั่นหมายความว่าคุณต้องคลายการบีบอัดไฟล์สองครั้ง
Andrew Henle

3
ข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่คุณทำเช่นนี้จะเป็นประโยชน์ ถ้ามันเป็นสิ่งที่ต่อเนื่องนั่นคือคุณบีบอัดไฟล์เป็นจำนวนมากอย่างสม่ำเสมอและในเวลาต่อมาจำเป็นต้องรู้จำนวนของเร็กคอร์ด - ทำไมไม่นับพวกมันเมื่อพวกมันถูกบีบอัดและฝังตัวเลขในชื่อไฟล์?
jamesqf

3
การอ่านไฟล์ 9.7GB จากดิสก์เชิงกลนั้นช้ากว่าปกติ จัดเก็บไฟล์บน SSD และดูว่า gunzip / zcat ทำงานเร็วขึ้นเท่าใด แต่ตามที่ @jamesqf พูดไว้ให้เก็บ linecount ในชื่อไฟล์หรือในไฟล์ใน tgz และการแตกไฟล์นั้นจะเร็วขึ้นมาก
ChuckCottrill

2
มีเหตุผลทางทฤษฎีที่ดีว่าทำไมคุณไม่สามารถหลีกเลี่ยงงานนี้ รูปแบบการบีบอัดที่ช่วยให้คุณตรวจสอบคุณสมบัติที่มีประโยชน์บางส่วนของข้อมูล "โดยไม่ต้องคลายมันว่า" สวยมากโดยความหมายไม่ดีรูปแบบการบีบอัดในขณะที่มันอาจจะ :)
ฮอบส์

คำตอบ:


28

sed, perlและawkคำสั่งที่คุณพูดถึงอาจจะถูกต้อง แต่พวกเขาทั้งหมดอ่านบีบอัดข้อมูลและตัวอักษรนับบรรทัดใหม่ในการที่ อักขระขึ้นบรรทัดใหม่เหล่านี้ไม่มีส่วนเกี่ยวข้องกับอักขระขึ้นบรรทัดใหม่ในข้อมูลที่ไม่บีบอัด

หากต้องการนับจำนวนบรรทัดในข้อมูลที่ไม่มีการบีบอัดจะไม่มีวิธีแก้ไขการบีบอัด วิธีการของคุณกับzcatเป็นแนวทางที่ถูกต้องและเนื่องจากข้อมูลที่มีขนาดใหญ่เช่นนั้นก็จะใช้เวลาในการยกเลิกการบีบอัดมัน

ยูทิลิตี้ส่วนใหญ่ที่เกี่ยวข้องกับการgzipบีบอัดและการคลายการบีบอัดมักจะใช้รูทีนไลบรารีที่แบ่งใช้แบบเดียวกันเพื่อทำเช่นนั้น วิธีเดียวที่จะเพิ่มความเร็วได้ก็คือการค้นหาการนำไปใช้ของรูทีนzlibที่เร็วกว่าค่าเริ่มต้นและสร้างใหม่เช่นzcatใช้


11
มันจะเป็นแบบฝึกหัดการโปรแกรมมิใช่เรื่องง่าย แต่เป็นไปได้ จุดรวมคือการไม่zcatสร้าง ส่วนสำคัญของงานzcatคือการสร้างผลผลิตจริง แต่ถ้าคุณนับ\nตัวอักษรเท่านั้นไม่จำเป็น gzipการบีบอัดเป็นหลักทำงานโดยการแทนที่สายยาวทั่วไปโดยสายสั้น ดังนั้นคุณจะต้องใส่ใจกับสายยาว ๆ ในพจนานุกรมที่มี a \nและนับการเกิด (ถ่วงน้ำหนัก) ของสิ่งเหล่านั้น เช่นเนื่องจากกฎภาษาอังกฤษ.\nเป็นสตริง 16 บิตทั่วไป
MSalters

19

ใช้ unpigz

คำตอบของ Kusalananda นั้นถูกต้องคุณจะต้องคลายการบีบอัดไฟล์ทั้งหมดเพื่อสแกนเนื้อหา /bin/gunzipทำสิ่งนี้ให้เร็วที่สุดเท่าที่จะทำได้บนแกนเดียว Pigzเป็นการใช้งานแบบขนานgzipซึ่งสามารถใช้หลายคอร์ได้

น่าเศร้าที่การบีบอัดตัวเองของไฟล์ gzip ปกติไม่สามารถ parallelized แต่pigzไม่ให้รุ่นปรับปรุงของgunzip, unpigzที่ทำงานที่เกี่ยวข้องเช่นการอ่านการเขียนและการ Checksumming ในหัวข้อที่แยกต่างหาก ในการวัดประสิทธิภาพอย่างรวดเร็วunpigzเกือบสองเท่าgunzipของ core i5 ของฉัน

ติดตั้งpigzกับผู้จัดการแพคเกจที่คุณชื่นชอบและใช้unpigzแทนgunzipหรือแทนunpigz -c zcatดังนั้นคำสั่งของคุณจะกลายเป็น:

$ unpigz -c T.csv.gz | wc -l

ทั้งหมดนี้ถือว่าคอขวดคือ CPU ไม่ใช่ดิสก์แน่นอน


4
pigzหน้าคนของฉันระบุว่าการบีบอัดไม่สามารถขนานกันได้อย่างน้อยก็ไม่มีกระแสที่ไม่ได้เตรียมไว้เป็นพิเศษสำหรับจุดประสงค์นั้น เป็นผลให้ pigz ใช้หัวข้อเดียว (หัวข้อหลัก) สำหรับการบีบอัด แต่จะสร้างสามหัวข้ออื่น ๆ สำหรับการอ่านการเขียนและการคำนวณการตรวจสอบซึ่งสามารถเพิ่มความเร็วในการบีบอัดภายใต้สถานการณ์บาง ยังคงเหมือนคุณฉันพบว่าอย่างน้อยสองครั้งเร็วกว่าgzipถ้าไม่ใช่เพราะความเท่าเทียม
Stéphane Chazelas

@ StéphaneChazelasจุดที่ดี! นั่นอธิบายถึงการเร่งความเร็วที่น่าผิดหวังเล็กน้อยสำหรับการคลายการบีบอัด ฉันแก้ไขโพสต์ของฉันเพื่อให้สะท้อนถึงข้อมูลนี้ดีขึ้น
marcelm

5

ปัญหาของท่อทั้งหมดคือคุณต้องเพิ่มงานเป็นสองเท่า ไม่ว่าการบีบอัดข้อมูลจะรวดเร็วแค่ไหนข้อมูลก็ยังต้องถูกปิดใช้งานไปยังกระบวนการอื่น

Perl มีPerlIO :: gzipซึ่งให้คุณอ่านสตรีม gzipped โดยตรง ดังนั้นจึงอาจมีข้อได้เปรียบแม้ว่าความเร็วในการบีบอัดอาจไม่ตรงกับunpigz:

#!/usr/bin/env perl

use strict;
use warnings;

use autouse Carp => 'croak';
use PerlIO::gzip;

@ARGV or croak "Need filename\n";

open my $in, '<:gzip', $ARGV[0]
    or croak "Failed to open '$ARGV[0]': $!";

1 while <$in>;

print "$.\n";

close $in or croak "Failed to close '$ARGV[0]': $!";

ฉันลองกับไฟล์ที่บีบอัด 13 MB gzip (คลายการบีบอัดถึง 1.4 GB) บนMacBook Pro รุ่นเก่าที่มี RAM 16 GBและThinkPad T400 รุ่นเก่าที่มี RAM 8 GBพร้อมไฟล์ในแคชอยู่แล้ว สำหรับ Mac สคริปต์ Perl นั้นเร็วกว่าการใช้ท่ออย่างมีนัยสำคัญ (5 วินาทีเทียบกับ 22 วินาที) แต่สำหรับ ArchLinux มันหายไปเป็น unpigz:

$ time -p ./gzlc.pl spy.gz 
1154737
จริง 4.49
ผู้ใช้ 4.47
sys 0.01

กับ

$ time -p unpigz -c spy.gz | ห้องสุขา -l
1154737
จริง 3.68
ผู้ใช้ 4.10
sys 1.46

และ

$ time -p zcat spy.gz | ห้องสุขา -l
1154737
จริง 6.41
ผู้ใช้ 6.08
sys 0.86

เห็นได้ชัดว่าการใช้unpigz -c file.gz | wc -lเป็นผู้ชนะที่นี่ทั้งในแง่ของความเร็ว และบรรทัดคำสั่งง่าย ๆ นั้นแน่นอนว่าการเขียนโปรแกรมนั้นสั้น


1
ฉันคิดว่าคุณประเมินทรัพยากรมากเกินความจำเป็นเพื่อย้ายข้อมูลระหว่างสองกระบวนการเปรียบเทียบกับการคำนวณการบีบอัด ลองเปรียบเทียบวิธีการต่างๆ;)
marcelm

2
@ SinanÜnürบนระบบ x86_64 Linux ของฉัน (ฮาร์ดแวร์เก่า) gzip | wcมีความเร็วเท่ากันกับสคริปต์ Perl ของคุณ และpigz | wcเร็วเป็นสองเท่า gzipทำงานด้วยความเร็วเท่ากันไม่ว่าฉันจะเขียนเอาต์พุตไปยัง / dev / null หรือไพพ์ลงในwcสิ่งที่ฉันเชื่อว่า "gzip library" ที่ใช้โดย perl นั้นเร็วกว่าเครื่องมือบรรทัดคำสั่ง gzip อาจมีปัญหาอื่นเกี่ยวกับท่อโดยเฉพาะกับ Mac / Darwin มันยังคงน่าทึ่งว่ารุ่น Perl นี้สามารถแข่งขันได้เลย
rudimeier

1
เมื่อวันที่ฉัน x86_64 ลินุกซ์ติดตั้งก็ดูเหมือนว่าจะทำดีกว่าและเลวร้ายยิ่งกว่าzcat unpigzฉันประหลาดใจที่ว่าไปป์ไลน์เร็วเท่าไรบนระบบ Linux เมื่อเทียบกับ Mac ฉันไม่ได้คาดหวังว่าแม้ว่าฉันควรจะมีเพราะฉันเคยสังเกตเห็นว่าโปรแกรมเดียวกันทำงานได้เร็วขึ้นบน CPU VM ที่ จำกัด Linux บน Mac เครื่องเดียวกันมากกว่าบนโลหะเปลือย
Sinan Ünür

1
นั่นดูน่าสนใจ; ในระบบของฉัน (Debian 8.8 amd64, quad core i5), สคริปต์ perl ช้าลงเล็กน้อย... ไฟล์ 109M .gz คลายการบีบอัดให้เหลือ 1.1G ของข้อความใช้เวลา 5.4 วินาทีต่อวินาทีzcat | wc -lและ 5.5 วินาทีสำหรับสคริปต์ perl ของคุณ สุจริตฉันประหลาดใจที่ผู้คนรายงานว่ามีการเปลี่ยนแปลงที่นี่โดยเฉพาะอย่างยิ่งระหว่าง Linux และ MacOS X!
marcelm

ฉันไม่รู้ว่าฉันจะสามารถสรุปสิ่งที่ฉันเห็นบน Mac ของฉันหรือไม่มีอะไรแปลก ๆ เกิดขึ้น ด้วยไฟล์ 1.4 GB ที่คลายการบีบอัดwc -lใช้เวลา 2.5 วินาที gzcat compressed.gz > /dev/nullใช้เวลา 2.7 วินาที กระนั้นท่อก็ใช้เวลา 22 วินาที ถ้าฉันลอง GNU wcจะใช้เวลาเพียงครึ่งวินาทีในไฟล์ที่คลายการบีบอัด แต่ใช้เวลา 22 วินาทีในการส่งไฟล์ GNU ใช้เวลายาวเป็นสองเท่าในการดำเนินการzcat zcat compressed.gz > /dev/nullนี่คือ Mavericks, CPU Core 2 Duo เก่า, RAM 16 GB, Crucial MX100 SSD
Sinan Ünür

4

คำตอบ Kusalananda เป็นส่วนใหญ่ที่ถูกต้อง ในการนับจำนวนบรรทัดคุณต้องค้นหาบรรทัดใหม่ อย่างไรก็ตามเป็นไปได้ในทางทฤษฎีในการค้นหาบรรทัดใหม่โดยไม่คลายการบีบอัดไฟล์อย่างสมบูรณ์

gzip ใช้การบีบอัด DEFLATE DEFLATE เป็นการรวมกันของการเข้ารหัส LZ77 และ Huffman อาจมีวิธีการคิดออกเพียงโหนดสัญลักษณ์ Huffman สำหรับการขึ้นบรรทัดใหม่และละเว้นส่วนที่เหลือ มีวิธีหนึ่งในการค้นหาบรรทัดใหม่ที่เข้ารหัสโดยใช้ L277 เก็บจำนวนไบต์และไม่สนใจทุกสิ่ง

ดังนั้น IMHO จึงมีความเป็นไปได้ในทางทฤษฎีในการหาวิธีแก้ปัญหาที่มีประสิทธิภาพมากกว่า unpigz หรือ zgrep ที่ถูกกล่าวว่ามันไม่ปฏิบัติ (เว้นแต่มีคนทำไปแล้ว)


7
ปัญหาสำคัญของแนวคิดนี้คือสัญลักษณ์ Huffman ที่ใช้โดย DEFLATE สอดคล้องกับลำดับของบิตหลังจากการบีบอัด LZ77 ดังนั้นอาจไม่มีความสัมพันธ์อย่างง่ายระหว่างพวกเขากับอักขระ U + 000A ในไฟล์ที่ไม่มีการบีบอัด ตัวอย่างเช่นสัญลักษณ์ Huffman หนึ่งอันอาจหมายถึง "ห้าบิตสุดท้าย" ตามด้วยสามบิตแรกของ "\ n" และสัญลักษณ์อื่นหมายถึงห้าบิตสุดท้ายของ "\ n" ตามด้วยแปดบิตทั้งหมดของ "T"
zwol

@zwol ไม่ส่วน LZ77 ของอัลกอริทึม Deflate บีบอัดลำดับไบต์ไม่ใช่ลำดับบิต en.wikipedia.org/wiki/DEFLATE#Duplicate_string_elimination
Ross Ridge

1
@RossRidge Huh ฉันไม่ทราบ แต่ฉันไม่คิดว่ามันจะขัดกับสิ่งที่ฉันพูด Huffmanสัญลักษณ์สามารถก็ปรากฏให้ฉันอยู่บนพื้นฐานของย่อหน้าถัดไปของการอ้างอิงที่ขยายตัวออกไปจำนวนตัวแปรของบิตแต่ละพวกเขาไม่ได้มีการผลิตจำนวนทั้งหมดของไบต์
zwol

1
@zwol แน่นอนคุณต้องค้นหาลำดับบิตของรหัส Huffman ในสตรีมบิต แต่คำตอบนี้ไม่ได้แนะนำเป็นอย่างอื่น ปัญหาของคำตอบนี้คือการพิจารณาว่ารหัส Huffman ใดที่สร้างขึ้นในที่สุดหรืออักขระขึ้นบรรทัดใหม่ไม่ใช่เรื่องง่าย รหัส LZ77 ที่สร้างบรรทัดใหม่กำลังเปลี่ยนแปลงอยู่ตลอดเวลาขณะที่หน้าต่างบานเลื่อนเลื่อนซึ่งหมายความว่ารหัส Huffman กำลังเปลี่ยนแปลงเช่นกัน คุณจะต้องใช้อัลกอริธึมการคลายการบีบอัดทั้งหมดยกเว้นส่วนเอาต์พุตและอาจเป็นส่วนหนึ่งของหน้าต่างแบบเลื่อนเนื่องจากคุณสนใจในการขึ้นบรรทัดใหม่เท่านั้น
Ross Ridge

1

สามารถทำได้โดยใช้ zgrepกับ-cธงและ$พารามิเตอร์

ในกรณีนี้ -c สั่งให้ออกจำนวนบรรทัดที่ตรงกันและ regex $ ตรงกับจุดสิ้นสุดของบรรทัดเพื่อให้ตรงกับทุกบรรทัดหรือไฟล์

zgrep -c $ T.csv.gz 

ตามที่แสดงความคิดเห็นโดย @ StéphaneChazelas - zgrepเป็นเพียงสคริปต์รอบตัวzcatและgrepควรให้ประสิทธิภาพที่คล้ายคลึงกับข้อเสนอแนะดั้งเดิมของzcat | wc -l


2
สวัสดี Yaron ขอบคุณสำหรับคำตอบแม้ zgrep จะใช้เวลามากที่สุดเท่าที่ zcat ฉันต้องการหาวิธีอื่นที่ฉันคิดว่า
Rahul

8
zgrepโดยทั่วไปเป็นสคริปต์ที่เรียกใช้zcat(เหมือนgzip -dcq) เพื่อคลายการบีบอัดข้อมูลและป้อนเข้าgrepจึงไม่ช่วย
Stéphane Chazelas

1
@ StéphaneChazelas - ขอบคุณสำหรับความคิดเห็นอัพเดตคำตอบของฉันเพื่อสะท้อน
Yaron

0

อย่างที่คุณเห็นคำตอบส่วนใหญ่พยายามปรับให้เหมาะสมที่สุด: จำนวนของสวิทช์บริบทและ IO ระหว่างกระบวนการ เหตุผลก็คือนี่เป็นสิ่งเดียวที่คุณสามารถเพิ่มประสิทธิภาพที่นี่ได้อย่างง่ายดาย

ตอนนี้ปัญหาคือความต้องการทรัพยากรของมันใกล้เคียงกับความต้องการทรัพยากรของการบีบอัด นี่คือเหตุผลที่การเพิ่มประสิทธิภาพจะไม่ทำให้เร็วขึ้นจริงๆ

หากสามารถเร่งความเร็วได้จริงจะเป็นอัลกอริทึม un-gzip (เช่นการคลายการบีบอัด) ซึ่งจะทำให้การผลิตจริงของสตรีมข้อมูลที่ถูกบีบอัด ค่อนข้างจะคำนวณเฉพาะจำนวนการขึ้นบรรทัดใหม่ในการสตรีมแตกจากการบีบอัดหนึ่ง มันยากที่จะต้องมีความรู้ลึกเกี่ยวกับอัลกอริทึมของ gzip (การรวมกันของอัลกอริทึมการบีบอัดLZWและHuffman ) มีความเป็นไปได้ค่อนข้างมากที่อัลกอริทึมไม่สามารถปรับเวลาการบีบอัดให้เหมาะสมกับการลดน้ำหนักอย่างมีนัยสำคัญซึ่งเราเพียงแค่ต้องรู้ว่าการนับบรรทัดใหม่ แม้ว่ามันจะเป็นไปได้โดยพื้นฐานแล้วไลบรารีการบีบอัด gzip ใหม่ควรได้รับการพัฒนา (มันไม่มีอยู่จนกระทั่งรู้)

คำตอบที่เป็นจริงสำหรับคำถามของคุณคือไม่คุณไม่สามารถทำให้เร็วขึ้นอย่างมีนัยสำคัญ

บางทีคุณอาจใช้การบีบอัด gzip แบบขนานบางอย่างถ้ามันมีอยู่ มันสามารถใช้หลายคอร์ซีพียูสำหรับการบีบอัด หากไม่มีอยู่ก็อาจพัฒนาได้ค่อนข้างง่าย

สำหรับxzนั้นมีคอมเพรสเซอร์แบบขนาน (pxz) อยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.