Warum ist gzip langsam, obwohl die CPU- und Festplattenleistung nicht voll ausgeschöpft wird?


14

Ich habe einige JSON-Dateien mit jeweils 20 GB, mit denen ich komprimieren möchte gzip:

gzip file1.json

Dies nimmt einen vollen CPU-Kern ein, alles in Ordnung.

Es verarbeitet ungefähr 25 MB / s (eingecheckt atop), meine Festplatte kann 125 MB / s lesen und ich habe 3 freie Prozessorkerne, sodass ich eine Beschleunigung beim parallelen Komprimieren mehrerer Dateien erwarte. Also laufe ich in anderen Terminals:

gzip file2.json
gzip file3.json
gzip file4.json

Überraschenderweise steigt mein Durchsatz nicht; Die CPU beträgt ungefähr 25% auf jedem Kern, und meine Festplatte liest immer noch nur mit 25 MB / s.

Warum und wie soll man das angehen?

Antworten:


17

Ich habe es herausgefunden:

Der Grund dafür ist, dass gzip(in Bezug auf die CPU-Geschwindigkeit im Vergleich zur HD-Suchgeschwindigkeit heutzutage) extrem niedrige Puffergrößen verwendet werden .

Es liest einige KB aus der Eingabedatei, komprimiert sie und leert sie in die Ausgabedatei. Angesichts der Tatsache, dass dies eine Festplattensuche erfordert, können nur wenige Vorgänge pro Sekunde ausgeführt werden.

Der Grund, warum meine Leistung nicht skaliert wurde, ist, dass man schon gzipwie verrückt gesucht hat.


Ich habe dies mit dem Unix- bufferDienstprogramm umgangen :

buffer -s 100000 -m 10000000 -p 100 < file1.json | gzip > file1.json.gz

Durch Puffern vieler Eingaben vor dem Senden an gzip kann die Anzahl kleiner Suchvorgänge drastisch reduziert werden. Die Optionen:

  • -sund -msollen die Größe des Puffers angeben (ich glaube, es ist in KB, aber nicht sicher)
  • -p 100 stellt sicher, dass die Daten erst an gzip übergeben werden, wenn der Puffer zu 100% gefüllt ist

Wenn vier davon parallel ausgeführt werden, kann ich erwartungsgemäß einen Durchsatz von 4 * 25 MB / s erzielen.


Ich frage mich immer noch, warum gzip es nicht erlaubt, die Puffergröße zu erhöhen - auf diese Weise ist es ziemlich nutzlos, wenn es auf einer sich drehenden Festplatte ausgeführt wird.

EDIT : Ich habe ein paar weitere Komprimierungsprogramme ausprobiert:

  • bzip2 verarbeitet aufgrund der stärkeren / CPU-intensiveren Komprimierung nur 2 MB / s
  • lzop scheint größere Puffer zuzulassen: 70 MB / s pro Kern und 2 Kerne können meine HD maximieren, ohne zu viel zu suchen

Kann dddas auch?
Simon Kuang

@ SimonKuang Ich vermute, dass dddas mit seiner bs=Option das gleiche kann , ja.
nh2

Klingt nach einem interessanten Zufall, dass für eine einzelne Datei die Blockgröße zufällig sowohl einen einzelnen CPU-Kern als auch die IOPS eines Laufwerks vollständig ausnutzte.
Dave L.

3

Nachdem ich mir die ersten fünf Vorlesungen in der MIT OpenCourseware für 6.172: "Performance Engineering von Softwaresystemen" angesehen hatte, führte ich den Linux-Leistungsanalysator "perf" auf einer mäßig großen Testdatei aus. Das Ergebnis scheint Pipeline-Stillstände anzuzeigen, bei denen ein Befehl auf das Ergebnis eines vorhergehenden warten muss.

       │         while (lookahead != 0) {                                                                
       │             /* Insert the string window[strstart .. strstart+2] in the                          
       │              * dictionary, and set hash_head to the head of the hash chain:                     
       │              */                                                                                 
       │             INSERT_STRING(strstart, hash_head);                                                 
  2.07 │       movzbl 0x8096d82(%edx),%eax                                                               
  3.99 │       mov    %edx,%ebp                                                                          
       │       shl    $0x5,%ecx                                                                          
  0.03 │       and    $0x7fff,%ebp                                                                       
  1.94 │       xor    %ecx,%eax                                                                          
  1.43 │       and    $0x7fff,%eax                                                                       
  2.01 │       mov    %eax,0x805e588                                                                     
  2.40 │       add    $0x8000,%eax                                                                      
  0.88 │       movzwl 0x8062140(%eax,%eax,1),%ecx                                                        
 23.79 │       movzwl %cx,%edi                                                                           
       │             /* Find the longest match, discarding those <= prev_length.  

Der vorletzte Befehl wird kopiert %ecxund der letzte muss warten (Blockieren der Pipeline), bis das %cxRegister Daten zur Verwendung bereit hat. Dieser Pipeline-Stall hält die enthaltende Schleife hoch.

Dies ist das Ergebnis eines wirklich obskuren C-Programmierstils der alten Schule.


1

Ein Tipp, der es auf einer Multi-Core- / Hyperthreading-CPU auf eine weitere Geschwindigkeitsstufe bringen könnte:
(unter der Annahme von Ubuntu)

sudo apt-get install moreutils

moreutils enthält unter anderem "gnu parallel" - mit vielen Optionen, mit denen Sie mehr von Ihrer CPU nutzen können.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.