“Wc -l” den daha hızlı bir şeye ihtiyacınız var


12

1GB gibi gerçekten büyük bir dosya wc -lyavaş olur. Belirli bir dosya için yeni satır sayısını hesaplamanın daha hızlı bir yolu var mı?


25
Daha hızlı diskler mi satın alıyorsunuz? Girdinin her bir baytının kendi 0x0Ayetersizliği açısından incelenmesi gerektiği göz önüne alındığında , G / Ç şüphesiz darboğazdır.
thrig

2
wcÇok fazla ek yüke sahip olduğunuzdan şüpheleniyorsanız , kendiniz uygulamayı deneyebilirsiniz foreach byte in file: if byte == '\n': linecount++. C veya montajcıda uygulanırsa, belki de en yüksek önceliğe sahip bir RTOS'ta çekirdek alanı dışında (veya bunun için bir kesme kullanıldığında), sistemle başka bir şey yapamazsınız. .. tamam, ben ;-))
Murphy

3
Ve sadece ölçek hakkında bir his almak için time wc -l some_movie.aviönbelleğe alınmamış bir dosyada hızlı bir sonuç yaptım 5172672 some_movie.avi -- real 0m57.768s -- user 0m0.255s -- sys 0m0.863s. Temelde @ thrig doğru olduğunu kanıtlar, I / O bu durumda performansınızı paramparça eder.
Murphy

10
Disk IO darboğazını göstermenin en iyi yolu, time wc -l some_large_file_smaller_than_cacheiki kez hızlı bir şekilde iki kez yapın ve ikinci işlemin ne kadar hızlı olduğunu time wc -l some_large_file_larger_than_cachegörün ve zamanlar arasındaki sürenin nasıl değişmediğini görün. Burada ~ 280MB'lık bir dosya için süre 1.7 saniyeden 0.2 saniyeye kadar gider, ancak 2GB'lık bir dosya için her iki seferde 14 saniyedir.
EightBitTony

1
Senin için ne kadar yavaş? Ne /usr/bin/time wc -l <file>diyor? Donanımınız nedir? Komutu tekrar tekrar çalıştırmanız daha hızlı mıdır? Gerçekten daha fazla bilgiye ihtiyacımız var;)
marcelm

Yanıtlar:


21

C ile yazmayı deneyebilirsiniz :

#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;
}

Örneğin ile kaydedin, örneğin wcl.cderleyin gcc wcl.c -O2 -o wclve ile çalıştırın

<yourFile ./wcl

Bu, yeni satırları sistemimdeki 1 GB'lık bir dosyaya yaklaşık 370 ms'de serpiştirir (tekrarlanan çalışır). (Arabellek boyutlarının artırılması beklenilen süreyi biraz arttırır - BUFSIZ en uygun seviyeye yakın olmalıdır). Bu ~ çok karşılaştırılabilir 380ms ben den alıyorum wc -l.

Mmaping bana yaklaşık 280ms'lik daha iyi bir zaman veriyor , ancak elbette gerçek dosyalarla sınırlı olma sınırlaması var (FIFOS yok, terminal girişi yok, vb.):

#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;
}

Test dosyamı aşağıdakilerle oluşturdum:

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

ve aşağıdakilerle bazı test satırları ekledi:

 $ echo >> 1GB 

ve bir hex editörü.


Mmap sonucu TBH'ye şaşırdım. Mmaping'in okuma / yazma işleminden daha hızlı olduğunu düşünürdüm, ama sonra bunun tersini gösteren bazı linux kriterleri gördüm. Bu durumda çok doğru gibi görünüyor.
PSkocik

4
mmap linux'da çok daha iyi sonuçlar alacak, çünkü bu günlerde büyük sayfalarla eşleşecek ve TLB özledikleri sloooowwwwwww.
jthill

Dosyanın farklı bölümlerini ayrı iş parçacıklarında (örneğin bir OpenMP fordöngüsüyle) okumanın bir yararı olabilir, böylece bir iş parçacığı giriş beklenirken biraz ilerleme kaydedilebilir. Ancak öte yandan, G / Ç zamanlamasını engelleyebilir, bu yüzden önerebileceğim tek şey denemek ve ölçmek!
Toby Speight

read()Versiyon salt önden yararlanabilir.
Barmar

1
@TobySpeight Evet, çoklu iş parçacığı bunu hızlandırabilir. Ayrıca 2 ^ 16 arama tabloları ile bir seferde iki bayt tarama seyir ben onunla son kez oldukça iyi bir hız sağladı.
PSkocik

18

@Pskocik tarafından önerilen çözüm sayısını, çağrı sayısını azaltarak geliştirebilirsiniz read. Bir 1Gb dosyasından parçaları okumak için birçok çağrı var BUFSIZ. Bunu yapmak için olağan yaklaşım, arabellek boyutunu artırmaktır:

  • sadece eğlence için, arabellek boyutunu 10 kat artırmayı deneyin. Veya 100. Debian 7'de BUFSIZ8192'dir. Orijinal programla, bu 120 bin okuma işlemidir. 100 faktörü azaltmak için muhtemelen 1Mb giriş tamponu alabilirsiniz.
  • daha uygun bir yaklaşım için, uygulamalar tek bir okuma işlemi gerektiren dosya kadar büyük bir tampon ayırabilir. Bu, "küçük" dosyalar için yeterince iyi çalışır (bazı okuyucuların makinelerinde 1 Gb'den fazla olmasına rağmen).
  • son olarak, tahsisi bu şekilde işleyen bellek eşlemeli G / Ç ile deney yapabilirsiniz.

Çeşitli yaklaşımları karşılaştırırken, bazı sistemlerin (Linux gibi) makinenizin kullanılmayan belleğinin çoğunu disk önbelleği olarak kullandığını unutmayın. Bir süre önce (neredeyse 20 yıl önce, aşağılık SSS'de bahsedildi ), bir metin düzenleyicide düşük bellek koşullarını ele almak için geliştirdiğim (çok iyi değil) bir sayfalama algoritmasından beklenmedik şekilde iyi sonuçlar aldım. Programın dosyayı okumak için kullanılan bellek tamponlarından çalıştığı için hızlı çalıştığı ve sadece dosya yeniden okunduğunda veya yazıldığında hızda bir fark olacağı açıklandı.

Aynı şey mmap(başka bir durumda hala bir SSS'ye dahil etmek için yapılacaklar listemde, bir geliştirici disk önbelleğinin iyileştirmenin gerçek nedeni olduğu bir senaryoda çok iyi sonuçlar bildirdi). Karşılaştırma ölçütlerinin geliştirilmesi, iyi (veya kötü) performansın nedenlerini analiz etmek için zaman ve özen gerektirir.

Daha fazla okuma:


2
Tampon boyutlarının belirli bir eşiğin üzerindeki etkisini fazla tahmin ediyorsunuz. Tipik olarak, tampon boyutunu 4KB-ish'den daha fazla arttırmak pek yardımcı olmaz ve aslında tamponu L1 önbelleğinden dışarı itebileceği için zararlı olabilir. Makinemde dd, 1MB arabellekleri kullanarak test yapmak 8KB'den daha yavaş . Wc için 8KB varsayılan değeri aslında oldukça iyi seçilmiştir, çok çeşitli sistemler için en uygun olana yakın olacaktır.
marcelm
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.