Cách nhanh nhất và hiệu quả nhất để lấy số lượng bản ghi (dòng) trong tệp nén gzip


16

Tôi đang cố gắng thực hiện đếm bản ghi trên tệp gzip 7.6 GB. Tôi tìm thấy một vài cách tiếp cận bằng cách sử dụng zcatlệnh.

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

Điều này hoạt động nhưng mất quá nhiều thời gian (hơn 10 phút để có được số đếm). Tôi đã thử một vài cách tiếp cận như

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

Tất cả ba lệnh này đang thực thi khá nhanh nhưng cho số đếm không chính xác là 28173811.

Làm thế nào tôi có thể thực hiện một số lượng hồ sơ trong một khoảng thời gian tối thiểu?


5
Tại sao bạn cần đếm số lượng hồ sơ? Nếu bạn đang cố gắng đếm chúng trước khi xử lý chúng, điều đó có nghĩa là bạn phải giải nén tệp hai lần.
Andrew Henle

3
Thông tin thêm về lý do tại sao bạn làm điều này sẽ hữu ích. Nếu đó là một cái gì đó đang diễn ra - nghĩa là, bạn thường xuyên nén một loạt các tệp và sau đó cần biết số lượng hồ sơ - tại sao không tính chúng là chúng được nén và nhúng số vào tên tệp?
jamesqf

3
Đọc một tệp 9,7 GB từ một đĩa cơ học vốn đã chậm hơn. Lưu trữ tệp trên ổ SSD và xem gunzip / zcat chạy nhanh hơn bao nhiêu. Nhưng như @jamesqf nói, lưu trữ linecount trong tên tệp hoặc trong một tệp trong tgz và trích xuất tệp đó sẽ nhanh hơn nhiều.
ChuckCottrill

2
Có những lý do lý thuyết tốt tại sao bạn không thể tránh công việc này. Một định dạng nén cho phép bạn xác định một số thuộc tính hữu ích của dữ liệu "mà không giải nén nó" là khá nhiều theo định nghĩa không phải là định dạng nén tốt như nó có thể :)
hobbs

Câu trả lời:


28

Các sed, perlawkcác lệnh mà bạn đề cập có thể đúng, nhưng tất cả đều đọc nén dữ liệu và số lượng ký tự xuống dòng trong đó. Các ký tự dòng mới này không liên quan gì đến các ký tự dòng mới trong dữ liệu không nén.

Để đếm số lượng dòng trong dữ liệu không nén, không có cách nào để giải nén nó. Cách tiếp cận của bạn với zcatlà cách tiếp cận chính xác và vì dữ liệu quá lớn, nên sẽ mất thời gian để giải nén nó.

Hầu hết các tiện ích liên quan đến gzipnén và giải nén rất có thể sẽ sử dụng cùng một thói quen thư viện dùng chung để làm như vậy. Cách duy nhất để tăng tốc nó là tìm ra cách thực hiện các zlibthói quen nhanh hơn so với các thói quen mặc định và xây dựng lại, ví dụ như zcatsử dụng chúng.


11
Nó sẽ là một bài tập lập trình không tầm thường, nhưng có thể làm được. Toàn bộ quan điểm là không xây dựng lại zcat. Một phần quan trọng của công việc zcatlà tạo ra đầu ra thực tế. Nhưng nếu bạn chỉ đếm các \nký tự, điều đó là không cần thiết. gzipnén về cơ bản hoạt động bằng cách thay thế các chuỗi dài phổ biến bằng các chuỗi ngắn hơn. Vì vậy, bạn chỉ cần quan tâm đến các chuỗi dài trong từ điển có chứa a \nvà đếm sự xuất hiện (có trọng số) của các chuỗi đó. Ví dụ do quy tắc tiếng Anh, .\nlà một chuỗi 16 bit phổ biến.
MSalters

19

Sử dụng unpigz.

Câu trả lời của Kusalananda là chính xác, bạn sẽ cần giải nén toàn bộ tập tin đó để quét nội dung của nó. /bin/gunziplàm điều này nhanh nhất có thể, trên một lõi đơn. Pigz là một triển khai song song gzipcó thể sử dụng nhiều lõi.

Đáng buồn thay, bản thân việc giải nén các tệp gzip bình thường không thể song song, nhưng pigzcung cấp một phiên bản cải tiến của gunzip, unpigzhoạt động liên quan như đọc, viết và kiểm tra trong một luồng riêng biệt. Trong một số điểm chuẩn nhanh, nhanh unpigzgấp gần hai lần so với gunzipmáy i5 lõi của tôi.

Cài đặt pigzvới trình quản lý gói yêu thích của bạn và sử dụng unpigzthay vì gunziphoặc unpigz -cthay vì zcat. Vì vậy, lệnh của bạn trở thành:

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

Tất cả điều này giả định nút cổ chai là CPU, không phải đĩa, tất nhiên.


4
pigzTrang người đàn ông của tôi nói rằng Giải nén không thể song song, ít nhất là không có các luồng khử phát được chuẩn bị đặc biệt cho mục đích đó. Do đó, pigz sử dụng một luồng duy nhất (luồng chính) để giải nén, nhưng sẽ tạo ra ba luồng khác để đọc, viết và kiểm tra tính toán, có thể tăng tốc độ giải nén trong một số trường hợp . Tuy nhiên, giống như bạn, tôi thấy nó nhanh hơn ít nhất gấp đôi gzip, nếu không phải vì sự song song
Stéphane Chazelas

@ StéphaneChazelas Điểm tốt! Điều đó giải thích sự tăng tốc đáng thất vọng nhẹ cho giải nén. Tôi đã chỉnh sửa bài viết của mình để phản ánh thông tin này tốt hơn.
marcelm

5

Vấn đề với tất cả các đường ống là về cơ bản bạn đang nhân đôi công việc. Cho dù giải nén nhanh đến mức nào, dữ liệu vẫn cần được chuyển sang một quy trình khác.

Perl có PerlIO :: gzip cho phép bạn đọc các luồng được nén trực tiếp. Do đó, nó có thể mang lại lợi thế ngay cả khi tốc độ giải nén của nó có thể không khớp với 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]': $!";

Tôi đã thử nó với tệp nén gzip 13 MB (giải nén thành 1,4 GB) trên MacBook Pro 2010với RAM 16 GBThinkPad T400với RAM 8 GB với tệp đã có trong bộ đệm. Trên máy Mac, tập lệnh Perl nhanh hơn đáng kể so với sử dụng đường ống (5 giây so với 22 giây), nhưng trên ArchLinux, nó đã thua unpigz:

$ time -p ./gzlc.pl spy.gz 
1154737
4,49 thật
người dùng 4,47
sys 0,01

đấu với

$ time -p unpigz -c spy.gz | wc -l
1154737
thực 3,68
người dùng 4.10
hệ thống 1.46

$ time -p zcat spy.gz | wc -l
1154737
6,41 thật
người dùng 6.08
hệ thống 0,86

Rõ ràng, sử dụng unpigz -c file.gz | wc -llà người chiến thắng ở đây cả về tốc độ. Và, dòng lệnh đơn giản đó chắc chắn đánh bại việc viết một chương trình, tuy nhiên ngắn.


1
Tôi nghĩ rằng bạn đang đánh giá quá cao các tài nguyên cần thiết để di chuyển dữ liệu giữa hai quy trình, so với các tính toán giải nén. Hãy thử điểm chuẩn các cách tiếp cận khác nhau;)
marcelm

2
@ SinanÜnür Trên hệ thống Linux x86_64 của tôi (cũng là phần cứng cũ) gzip | wccó cùng tốc độ so với tập lệnh perl của bạn. Và pigz | wcnhanh gấp đôi. gzipchạy với cùng tốc độ, bất kể tôi viết đầu ra thành / dev / null hay pipe vào wcĐiều tôi tin là "thư viện gzip" được sử dụng bởi perl nhanh hơn công cụ dòng lệnh gzip. Có thể có một vấn đề cụ thể khác của Mac / Darwin với đường ống. Thật đáng ngạc nhiên khi phiên bản perl này hoàn toàn cạnh tranh.
rudimeier

1
Trên bản cài đặt Linux x86_64 của tôi, nó dường như làm tốt hơn zcatvà tệ hơn unpigz. Tôi ngạc nhiên khi thấy đường ống trên hệ thống Linux nhanh hơn nhiều so với máy Mac. Tôi không mong đợi điều đó, mặc dù tôi nên có một lần khi tôi quan sát cùng một chương trình chạy nhanh hơn trên máy ảo Linux giới hạn CPU trên cùng máy Mac đó so với kim loại trần.
Sinan Ünür

1
Nó thật thú vị; trên hệ thống của tôi (Debian 8,8 amd64, lõi tứ i5), tập lệnh perl chậm hơn một chút ... tập tin 109M .gz giải nén thành 1.1G văn bản, liên tục mất 5,4 giây zcat | wc -lvà 5,5 giây cho tập lệnh perl của bạn. Thành thật mà nói, tôi rất ngạc nhiên về sự thay đổi mà mọi người đang báo cáo ở đây, đặc biệt là giữa Linux và MacOS X!
marcelm

Tôi không biết nếu tôi có thể khái quát những gì tôi đang thấy trên máy Mac của mình, điều gì đó kỳ lạ đang diễn ra. Với tệp 1,4 GB được giải nén, wc -lmất 2,5 giây. gzcat compressed.gz > /dev/nullmất 2,7 giây. Tuy nhiên, đường ống mất 22 giây. Nếu tôi thử GNU wc, chỉ mất nửa giây trên tệp được giải nén, nhưng 22 giây trong đường ống. GNU zcatmất gấp đôi thời gian để thực thi zcat compressed.gz > /dev/null. Đây là trên Mavericks, CPU Core 2 Duo cũ, RAM 16 GB, SSD MX100 quan trọng.
Sinan ürnür

4

Câu trả lời của Kusalananda hầu hết là đúng. Để đếm dòng bạn cần tìm kiếm dòng mới. Tuy nhiên về mặt lý thuyết có thể tìm kiếm các dòng mới mà không giải nén hoàn toàn tệp.

gzip sử dụng nén DEFLATE. DEFLATE là sự kết hợp của mã hóa LZ77 và Huffman. Có thể có một cách để chỉ ra nút biểu tượng Huffman cho dòng mới và bỏ qua phần còn lại. Gần như chắc chắn có một cách để tìm kiếm các dòng mới được mã hóa bằng L277, giữ số byte và bỏ qua mọi thứ khác.

Vì vậy, IMHO về mặt lý thuyết có thể đưa ra một giải pháp hiệu quả hơn unpigz hoặc zgrep. Điều đó được nói rằng nó chắc chắn không thực tế (trừ khi ai đó đã làm điều đó).


7
Một vấn đề lớn với ý tưởng này là, các ký hiệu Huffman được DEFLATE sử dụng tương ứng với các chuỗi bit sau khi nén LZ77, do đó, có thể không có mối quan hệ đơn giản giữa chúng và các ký tự U + 000A trong tệp không nén. Chẳng hạn, có thể một biểu tượng Huffman có nghĩa là năm bit cuối cùng của "." theo sau là ba bit đầu tiên của "\ n" và một ký hiệu khác có nghĩa là năm bit cuối cùng của "\ n" được theo sau bởi tất cả tám bit của "T".
zwol

@zwol Không, phần LZ77 của thuật toán Deflate nén các chuỗi byte, không phải các chuỗi bit. vi.wikipedia.org/wiki/DEFLATE#D repeatate_opes_ Friination
Ross Ridge

1
@RossRidge Huh, tôi không biết điều đó, nhưng tôi không nghĩ nó làm mất hiệu lực những gì tôi nói. Các biểu tượng Huffman có thể, nó xuất hiện với tôi dựa trên đoạn tiếp theo của tài liệu tham khảo đó, mỗi phần mở rộng thành một số bit khác nhau, chúng không phải tạo ra toàn bộ số byte.
zwol

1
@zwol Chắc chắn, bạn phải tìm kiếm các chuỗi bit mã Huffman phù hợp trong luồng bit nhưng câu trả lời này không đề xuất khác. Vấn đề với câu trả lời này là việc xác định mã Huffman nào cuối cùng tạo ra hoặc nhiều ký tự dòng mới không đơn giản. Các mã LZ77 tạo ra các dòng mới liên tục thay đổi khi cửa sổ trượt di chuyển, điều đó có nghĩa là các mã Huffman cũng đang thay đổi. Bạn sẽ phải thực hiện toàn bộ thuật toán giải nén trừ phần đầu ra và có thể một phần của cửa sổ trượt vì bạn chỉ quan tâm đến các dòng mới.
Ross Ridge

1

Có thể được thực hiện bằng cách sử dụng zgrepvới -ccờ và $tham số.

Trong trường hợp này -c hướng dẫn lệnh xuất số dòng phù hợp và regex $ khớp với cuối dòng để nó khớp với mọi dòng hoặc tệp.

zgrep -c $ T.csv.gz 

Theo nhận xét của @ StéphaneChazelas - zgrepchỉ là một kịch bản xung quanh zcatgrepvà nó sẽ cung cấp hiệu suất tương tự như đề nghị ban đầu củazcat | wc -l


2
Xin chào Yaron cảm ơn vì câu trả lời ngay cả zgrep cũng mất nhiều thời gian như zcat tôi cần tìm một cách tiếp cận khác mà tôi nghĩ
Rahul

8
zgrepnói chung là một tập lệnh gọi zcat(giống như gzip -dcq) để giải nén dữ liệu và đưa dữ liệu vào grep, vì vậy sẽ không có ích.
Stéphane Chazelas

1
@ StéphaneChazelas - cảm ơn vì nhận xét, cập nhật câu trả lời của tôi để phản ánh nó.
Yaron

0

Như bạn có thể thấy, hầu hết các câu trả lời đều cố gắng tối ưu hóa những gì nó có thể: số lượng các công tắc ngữ cảnh và IO liên quy trình. Lý do là, đây là điều duy nhất bạn có thể tối ưu hóa ở đây một cách dễ dàng.

Bây giờ vấn đề là nhu cầu tài nguyên của nó gần như không đáng kể so với nhu cầu tài nguyên của giải nén. Đây là lý do tại sao việc tối ưu hóa sẽ không thực sự làm mọi thứ nhanh hơn.

Trong trường hợp nó có thể thực sự được tăng tốc, đó sẽ là một thuật toán un-gzip (nghĩa là giải nén) đã sửa đổi, loại bỏ việc sản xuất thực tế của luồng dữ liệu được giải nén; thay vào đó, nó chỉ tính toán số lượng dòng mới trong luồng được giải nén từ dòng được nén . Thật khó, nó sẽ đòi hỏi kiến ​​thức sâu về thuật toán của gzip (một số kết hợp thuật toán nén LZWHuffman ). Một điều khá có thể xảy ra là thuật toán không thể tối ưu hóa đáng kể thời gian giải nén với việc làm sáng, mà chúng ta chỉ cần biết số lượng dòng mới. Ngay cả nếu có thể, về cơ bản, một thư viện giải nén gzip mới đã được phát triển (nó không tồn tại cho đến khi biết).

Câu trả lời thực tế cho câu hỏi của bạn là, không, bạn không thể làm cho nó nhanh hơn đáng kể.

Có lẽ bạn có thể sử dụng một số giải nén gzip song song, nếu nó tồn tại. Nó có thể sử dụng nhiều lõi CPU để giải nén. Nếu nó không tồn tại, nó có thể được phát triển tương đối dễ dàng.

Đối với xz , tồn tại một máy nén song song (pxz).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.