Bir gzip sıkıştırılmış dosyada kayıt sayısını (satır) almanın en hızlı ve en etkili yolu


16

7,6 GB gzip dosyasında bir kayıt sayısı yapmaya çalışıyorum. zcatKomutu kullanarak birkaç yaklaşım buldum .

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

Bu işe yarıyor ama çok fazla zaman alıyor (sayımı almak için 10 dakikadan fazla). Gibi birkaç yaklaşım denedim

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

Bu komutların üçü de oldukça hızlı çalışıyor ancak yanlış bir sayı 28173811 veriyor.

Kayıt sayımını en kısa sürede nasıl yapabilirim?


5
Neden kayıt sayısını saymanız gerekiyor? Onları işlemeden önce saymaya çalışıyorsanız, dosyayı iki kez sıkıştırmanız gerekir.
Andrew Henle

3
Bunu neden yaptığınız hakkında daha fazla bilgi yardımcı olacaktır. Devam eden bir şey varsa - yani, düzenli olarak bir grup dosyayı sıkıştırırsınız ve daha sonra bazı kayıtların sayısını bilmeniz gerekir - bunları sıkıştırıldıkça saymıyorsunuz ve sayıyı dosya adına katıştırmıyorsunuz?
jamesqf

3
Mekanik bir diskten 9.7GB'lık bir dosyayı okumak doğal olarak daha yavaştır. Dosyayı bir SSD'de saklayın ve gunzip / zcat'in ne kadar hızlı çalıştığını görün. Ancak @jamesqf'in söylediği gibi, satır numarasını dosya adında veya tgz'deki bir dosyada saklayın ve bu dosyayı ayıklamak çok daha hızlı olacaktır.
ChuckCottrill

2
Bu işten kaçınamamanızın iyi teorik nedenleri var. Eğer "it decompressing olmadan" bazı verilerin kullanışlı özelliği belirlemek sağlayan bir sıkıştırma formatı :) iyi bir sıkıştırma formatı olması gerektiği gibi değil, tanımı gereği hemen hemen
Hobbs

Yanıtlar:


29

Bahsettiğiniz sed, perlve awkkomutları doğru olabilir, ancak hepsi sıkıştırılmış verileri okur ve içindeki yeni satır karakterlerini sayar. Bu satırsonu karakterlerinin, sıkıştırılmamış verilerdeki satırsonu karakterleriyle hiçbir ilgisi yoktur.

Sıkıştırılmamış verilerdeki satır sayısını saymak için, sıkıştırmayı kaldırmanın bir yolu yoktur. Your yaklaşım zcatdoğru bir yaklaşımdır ve veri çok büyük olduğu için, bu olacak sıkıştırmayı zaman ayırın.

gzipSıkıştırma ve açma işlemleriyle ilgilenen çoğu yardımcı program , büyük olasılıkla bunu yapmak için aynı paylaşılan kitaplık yordamlarını kullanır. Hızlandırmanın tek yolu zlib, varsayılanlardan bir şekilde daha hızlı olan rutinlerin bir uygulamasını bulmak ve örneğin zcatbunları kullanmak için yeniden oluşturmak olacaktır.


11
Önemsiz bir programlama alıştırması olabilir, ancak yapılabilir. Bütün mesele yeniden inşa etmemekzcat . Çalışmalarının önemli bir kısmı zcatgerçek çıktıyı üretmektir. Ancak sadece \nkarakterleri sayıyorsanız , bu gerekli değildir. gzipsıkıştırma esas olarak uzun dizeleri daha kısa dizelerle değiştirerek çalışır. Bu nedenle, sözlükte yalnızca a içeren uzun dizeleri önemsemeniz \nve bunların (ağırlıklı) oluşumunu saymanız gerekir. İngilizce kurallar nedeniyle, .\n16 bit yaygın bir dizedir.
MSalters

19

Unpigz kullanın.

Kusalananda cevabı doğrudur, sen olacaktır tüm dosya içeriğini taramak için bu sıkıştırmayı gerekir. /bin/gunzipbunu olabildiğince hızlı, tek bir çekirdek üzerinde yapar. Pigz , gzipbirden fazla çekirdek kullanabilen paralel bir uygulamadır .

Ne yazık ki, normal bir gzip dosyalarının dekompresyon kendisi parallelized ancak edilemez pigzteklif geliştirilmiş bir versiyonu yapar gunzip, unpigzböyle, okuma yazma ve ayrı bir konu checksumming gibi ilgili çalışır söyledi. Bazı hızlı ölçütlerde, çekirdek i5 makinemden unpigzneredeyse iki kat daha hızlı gunzip.

pigzFavori paket yöneticinizle kurun ve unpigzyerine gunzipveya unpigz -cyerine kullanın zcat. Böylece komutunuz:

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

Bütün bunlar darboğazın elbette disk değil CPU olduğunu varsayar.


4
Benim pigzman sayfam, Dekompresyonun paralelleştirilemeyeceğini, en azından bu amaçla özel olarak hazırlanmış söndürme akımları olmadan değil. Sonuç olarak, pigz dekompresyon için tek bir iplik (ana iplik) kullanır, ancak bazı durumlarda dekompresyonu hızlandırabilen okuma, yazma ve kontrol hesaplama için diğer üç iplik oluşturur . Yine de, sizin gibi gzip, paralellikten dolayı olmasa bile, en az iki kat daha hızlı buluyorum
Stéphane Chazelas

@ StéphaneChazelas İyi bir nokta! Bu, dekompresyon için hafif hayal kırıklığı yaratan hızlandırmayı açıklar. Bu bilgileri daha iyi yansıtacak şekilde yazımı düzenledim.
marcelm

5

Tüm boru hatları ile ilgili sorun, aslında işi iki katına çıkarmanızdır. Dekompresyon ne kadar hızlı olursa olsun, verinin yine de başka bir işleme geçirilmesi gerekir.

Perl, doğrudan sıkıştırılmış akışları okumanızı sağlayan PerlIO :: gzip'e sahiptir. Bu nedenle, dekompresyon hızı aşağıdakilerden farklı olsa bile bir avantaj sunabilir 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]': $!";

16 GB RAM ile eski bir 2010 MacBook Pro ve zaten önbellekte dosya ile 8 GB RAM ile eski bir ThinkPad T400 13 MB gzip sıkıştırılmış dosya (1.4 GB'a kadar açılır) ile denedim . Mac'te Perl betiği, boru hatlarını kullanmaktan önemli ölçüde daha hızlıydı (22 saniyeye karşı 22 saniye), ancak ArchLinux'da unpigz'e kaybetti:

$ time -p ./gzlc.pl spy.gz 
1154737
gerçek 4.49
4.47 kullanıcısı
sys 0.01

e karşı

$ time -p unpigz -c casus.gz | wc -l
1154737
gerçek 3.68
kullanıcı 4.10
sys 1.46

ve

$ time -p zcat Instagram Hesabındaki Resim ve Videoları spy.gz | wc -l
1154737
gerçek 6.41
6.08 kullanıcısı
sys 0.86

Açıkçası, unpigz -c file.gz | wc -lburada hem hız açısından kazanan kazanıyor. Ve bu basit komut satırı, kısa olsa da, kesinlikle bir program yazmayı yener.


1
Dekompresyon hesaplamalarına kıyasla, verileri iki işlem arasında taşımak için gereken kaynakları çok fazla tahmin ettiğinizi düşünüyorum. Çeşitli yaklaşımları karşılaştırmayı deneyin;)
marcelm

2
@ SinanÜnür x86_64 Linux sistemimde (eski donanım) gzip | wcda perl betiğinizle aynı hızda. Ve pigz | wchızlı. gzip/ dev / null veya pipe içine çıktı yazsam da aynı hızda çalışır. wcİnandığım şey perl tarafından kullanılan "gzip kütüphanesinin" gzip komut satırı aracından daha hızlı olduğudur. Belki de borularda Mac / Darwin'e özgü başka bir sorun var. Bu perl versiyonunun rekabetçi olması hala şaşırtıcı.
rudimeier

1
X86_64 Linux kurulumumda, bundan daha iyi zcatve daha kötü gibi görünüyor unpigz. Linux sisteminde boru hattının Mac'e kıyasla ne kadar hızlı olduğuna şaşıyorum. Bir keresinde gözlemlediğim gibi, aynı programın aynı Mac'te CPU ile sınırlı bir Linux VM'de çıplak metalden daha hızlı çalışmasını beklemiyordum.
Sinan Ünür

1
İlginç; sistemimde (Debian 8.8 amd64, dört çekirdekli i5) üzerine, perl komut hafifçe olduğunu yavaş ... 109m .gz dosya metnin 1.1g için açılırken, sürekli için 5.4 saniye sürer zcat | wc -l, ve Perl komut dosyası için 5.5 sn. Dürüst olmak gerekirse, insanların özellikle Linux ve MacOS X arasında bildirdiği varyasyona hayran kaldım!
marcelm

Mac'imde gördüğümü genelleştirip genelleştiremeyeceğimi bilmiyorum, tuhaf bir şeyler oluyor. Sıkıştırılmış 1,4 GB dosya ile wc -l2,5 saniye sürer. gzcat compressed.gz > /dev/null2.7 saniye sürer. Ancak, boru hattı 22 saniye sürüyor. GNU'yu denersem wc, sıkıştırılmış dosyada sadece yarım saniye, boru hattında 22 saniye sürer. GNU'nun zcatyürütülmesi iki kat daha uzun sürer zcat compressed.gz > /dev/null. Bu Mavericks, eski Core 2 Duo CPU, 16 GB RAM, Crucial MX100 SSD'de.
Sinan Ünür

4

Kusalananda'nın cevabı çoğunlukla doğrudur. Satırları saymak için yeni satır aramanız gerekir. Ancak teorik olarak dosyayı tamamen açmadan yeni satır aramak mümkündür.

gzip, DEFLATE sıkıştırmayı kullanır. DEFLATE, LZ77 ve Huffman kodlamasının bir kombinasyonudur. Yeni satır için sadece Huffman sembol düğümünü bulmanın ve geri kalanını görmezden gelmenin bir yolu olabilir. L277 kullanılarak kodlanmış yeni satırları aramanın, bayt sayımını tutmanın ve diğer her şeyi görmezden gelmenin bir yolu var.

Yani IMHO teorik olarak unpigz veya zgrep'ten daha verimli bir çözüm bulmak mümkün. Olduğu söyleniyor, kesinlikle pratik değil (birisi daha önce yapmadıysa).


7
Bu fikirle ilgili önemli bir sorun, DEFLATE tarafından kullanılan Huffman sembolleri , LZ77 sıkıştırmasından sonra bit dizilerine karşılık gelir , bu nedenle bunlar ile sıkıştırılmamış dosyadaki U + 000A karakterleri arasında basit bir ilişki olmayabilir. Örneğin, belki bir Huffman sembolü "." ardından "\ n" nin ilk üç biti ve başka bir sembol "\ n" nin son beş biti ve ardından "T" nin sekiz bitinin tümü anlamına gelir.
zwol

@zwol Hayır, Deflate algoritmasının LZ77 kısmı bit dizilerini değil, bayt dizilerini sıkıştırır. en.wikipedia.org/wiki/DEFLATE#Duplicate_string_elimination
Ross Ridge

1
@RossRidge Huh, bunu bilmiyordum, ama söylediklerimi geçersiz kıldığını sanmıyorum. Huffman can, o referans sonraki paragrafta dayalı bana görünen semboller, her bit değişken sayıda genişletmek, bunlar bayt tam sayı üretmek gerekmez.
zwol

1
@zwol Elbette, bit akışında eşleşen Huffman kod bit dizilerini aramak zorundasınız, ancak bu cevap aksini önermiyor. Bu cevabın problemi, hangi Huffman kodlarının nihayetinde üreteceğini veya daha fazla yeni satır karakteri belirlemenin basit olmamasıdır. Yeni satırlar oluşturan LZ77 kodları, sürgülü pencere hareket ettikçe sürekli olarak değişmektedir, bu da Huffman kodlarının da değiştiği anlamına gelmektedir. Çıktı kısmı hariç tüm dekompresyon algoritmasını ve belki de yeni satırlarla ilgilendiğiniz için sürgülü pencerenin bir kısmını uygulamanız gerekir.
Ross Ridge

1

Flag ve parametre zgrepile yapılabilir .-c$

Bu durumda -c komutuna eşleşen satırların sayısını vermesi talimatını verin ve regex $ satırın sonuna eşleşir, böylece her satır veya dosyayla eşleşir.

zgrep -c $ T.csv.gz 

@ StéphaneChazelas tarafından yorumlandığı gibi - zgrepsadece bir senaryo zcatve greporijinal önerisine benzer bir performans sağlamalıdırzcat | wc -l


2
Merhaba Yaron cevap için teşekkürler zgrep bile zcat sanırım başka bir yaklaşım bulmak gerekir kadar zaman alıyor
Rahul

8
zgrepgenellikle verileri açmak ve beslemek için çağrılan zcat(aynı gzip -dcq) komut dosyasıdır grep, bu yüzden yardımcı olmaz.
Stéphane Chazelas

1
@ StéphaneChazelas - Yorum için teşekkürler, cevabımı yansıtmak için güncelleyin.
Yaron

0

Gördüğünüz gibi, çoğu yanıt mümkün olanı optimize etmeye çalışır: bağlam anahtarlarının sayısı ve süreçler arası ES. Bunun nedeni, burada kolayca optimize edebileceğiniz tek şey bu.

Şimdi sorun, kaynak ihtiyacının dekompresyonun kaynak ihtiyacı için neredeyse ihmal edilebilir olmasıdır. Bu yüzden optimizasyonlar hiçbir şeyi daha hızlı yapmaz.

Gerçekten hızlandırılabildiği yerlerde, sıkıştırılmış veri akışının gerçek üretimini dışarıda bırakan değiştirilmiş bir un-gzip (yani dekompresyon) algoritması olacaktır; bunun yerine sadece yeni satır sayısını hesaplar açılmış akımından gelen sıkıştırılmış bir. Zor olurdu, gzip'in algoritması hakkında derin bilgi gerektirir ( LZW ve Huffman'ın bir kombinasyonu sıkıştırma algoritmalarının bir ). Algoritmanın, dekompresyon süresini aydınlatma ile önemli ölçüde optimize etmeyi mümkün kılmadığı, sadece satırsonu sayılarını bilmemiz oldukça olasıdır. Mümkün olsa bile, aslında yeni bir gzip dekompresyon kütüphanesi geliştirilmelidir (bilinene kadar mevcut değildir).

Sorunuzun gerçekçi cevabı, hayır, önemli ölçüde daha hızlı yapamazsınız.

Belki de varsa, paralelleştirilmiş bir gzip dekompresyonu kullanabilirsiniz. Dekompresyon için birden fazla CPU çekirdeği kullanabilir. Eğer mevcut değilse, nispeten kolay bir şekilde geliştirilebilir.

İçin , XZ , paralel bir kompresör (pxz) vardır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.