Informieren Sie sich über Bibliotheken, die bzip2 in .NET Framework unterstützen. Vergleichen Sie die Verarbeitungsgeschwindigkeit mit Python und bzcat.
Dieser Artikel hat eine Python-Version.
Ich habe drei verschiedene Bibliotheken gefunden, die bzip2 unterstützen.
Überprüfen Sie, wie lange diese Bibliotheken Multistreams verarbeiten und bereitstellen.
Daten, die einzeln mit bzip2 komprimiert und verkettet werden, werden als Multi-Stream bezeichnet.
Erstellungsbeispiel
$ echo -n hello | bzip2 > a.bz2
$ echo -n world | bzip2 > b.bz2
$ cat a.bz2 b.bz2 > ab.bz2
Es kann so behandelt werden, wie es ist, mit Befehlen wie "bzcat".
$ bzcat ab.bz2
helloworld
Es wird für die parallele Komprimierung pbzip2 und für Wikipedia-Dumps verwendet.
[Addition] Dasselbe kann mit gzip gemacht werden, was im folgenden Artikel untersucht wird.
SharpZipLib
Unterstützt mehrere Komprimierungsalgorithmen.
Die bzip2-Implementierung ist unten.
Wir haben bestätigt, dass bzip2 nur mit den folgenden 6 Dateien extrahiert werden kann.
Verwenden Sie die von NuGet erhaltene Bibliothek anstelle der Mindestkonfiguration.
nuget install SharpZipLib
cp SharpZipLib.1.2.0/lib/net45/ICSharpCode.SharpZipLib.dll .
Versuchen Sie, ab.bz2 zu erweitern, das als Multi-Stream-Beispiel erstellt wurde.
#r "SharpZipLib.1.2.0/lib/net45/ICSharpCode.SharpZipLib.dll"
open System.IO
open ICSharpCode.SharpZipLib.BZip2
do
use fs = new FileStream("ab.bz2", FileMode.Open)
use bz = new BZip2InputStream(fs)
use sr = new StreamReader(bz)
printfn "%s" (sr.ReadToEnd())
hello
Es wird nur der erste Stream verarbeitet.
Wenn Sie sich BZip2InputStream.cs ansehen, scheint es kein Multi-Stream zu unterstützen, daher müssen Sie es nacheinander lesen.
#r "SharpZipLib.1.2.0/lib/net45/ICSharpCode.SharpZipLib.dll"
open System.IO
open ICSharpCode.SharpZipLib.BZip2
do
use fs = new FileStream("ab.bz2", FileMode.Open)
while fs.Position < fs.Length do
use bz = new BZip2InputStream(fs, IsStreamOwner = false)
use sr = new StreamReader(bz)
printfn "%s" (sr.ReadToEnd())
hello
world
SharpCompress
Unterstützt mehrere Komprimierungsalgorithmen.
Die bzip2-Implementierung ist unten.
Dieses Verzeichnis kann extrahiert und unabhängig verwendet werden. Das einzige ist, dass es keine eigene Definition von "CompressionMode" gibt, aber den vorhandenen Aufzählungstyp ersetzt.
BZip2Stream.cs (zusätzlich)
using System.IO.Compression;
Wir haben bestätigt, dass bzip2 nur mit den folgenden drei Dateien extrahiert werden kann.
Die Klasse CBZip2InputStream ist jedoch "intern" und muss daher "öffentlich" sein.
Verwenden Sie die von NuGet erhaltene Bibliothek anstelle der Mindestkonfiguration.
nuget install sharpcompress
cp SharpCompress.0.25.1/lib/net46/SharpCompress.dll .
Versuchen Sie, ab.bz2 zu erweitern, das als Multi-Stream-Beispiel erstellt wurde.
#r "SharpCompress.dll"
open System.IO
open SharpCompress.Compressors
do
use fs = new FileStream("ab.bz2", FileMode.Open)
use bz = new BZip2.BZip2Stream(fs, CompressionMode.Decompress, true)
use sr = new StreamReader(bz)
printfn "%s" (sr.ReadToEnd())
Ausführungsergebnis
helloworld
Mit Unterstützung für Multi-Stream konnte ich alles auf einmal lesen.
Wenn Sie das Multistream-Flag auf "false" setzen, wird "fs" geschlossen, wenn das Ende eines Streams erreicht ist. Wenn man sich CBZip2InputStream.cs ansieht, scheint es nicht offen zu bleiben. Daher scheint es, dass die einzige Möglichkeit, es nacheinander zu lesen, darin besteht, etwas zu tun, das "Entsorgen" ignoriert.
#r "SharpCompress.dll"
open System.IO
open SharpCompress.Compressors
do
let mutable ignore = true
use fs = { new FileStream("ab.bz2", FileMode.Open) with
override __.Dispose disposing = if not ignore then base.Dispose disposing }
while fs.Position < fs.Length do
use bz = new BZip2.BZip2Stream(fs, CompressionMode.Decompress, false)
use sr = new StreamReader(bz)
printfn "%s" (sr.ReadToEnd())
ignore <- false
Ausführungsergebnis
hello
world
AR.Compression.BZip2
Im Gegensatz zu anderen Bibliotheken ist es auf bzip2 spezialisiert. Dekomprimierung und Komprimierung werden in einer Klasse implementiert, sodass wir die Mindestkonfiguration nicht separat betrachten.
Es ist in NuGet registriert.
Das Teilen der Bibliothek mit Mono erfordert ein wenig Arbeit, daher werde ich sie dieses Mal selbst erstellen, anstatt NuGet zu verwenden.
Benennen Sie die in P / Invoke angegebene DLL um.
10: private const string DllName = "libbz2";
Dies verwendet libbz2.dll unter Windows und libbz2.so unter WSL. Die WSL verweist auf /usr/lib/libbz2.so, auch wenn sie sich nicht im aktuellen Verzeichnis befindet.
Erstellen Sie die DLL.
csc -o -out:AR.Compression.BZip2.dll -t:library -unsafe sources/AR.BZip2/*.cs
Bzip2 für Windows verwendet die unten verteilten Binärdateien.
Versuchen Sie, ab.bz2 zu erweitern, das als Multi-Stream-Beispiel erstellt wurde.
#r "AR.Compression.BZip2.dll"
open System.IO
open System.IO.Compression
do
use fs = new FileStream("ab.bz2", FileMode.Open)
use bz = new BZip2Stream(fs, CompressionMode.Decompress, false)
use sr = new StreamReader(bz)
printfn "%s" (sr.ReadToEnd())
Ausführungsergebnis
helloworld
Alle Streams wurden gleichzeitig erweitert. Wenn man sich BZip2Stream.cs ansieht, scheint es, dass es nicht sequentiell gelesen werden soll. In Anbetracht der Behandlung des Basisstroms, die als nächstes zu sehen sein wird, scheint es, dass er nicht ohne Änderung behandelt werden kann.
Überprüfen Sie in jeder Bibliothek, wo der übergebene Basisstrom gelesen wird.
Stream in .NET wird als Basis-Stream bezeichnet, um ihn von Stream im Sinne von bzip2 zu unterscheiden.
SharpZipLib: src/ICSharpCode.SharpZipLib/BZip2/BZip2InputStream.cs
417: thech = baseStream.ReadByte();
231: int magic0 = bsStream.ReadByte();
232: int magic1 = bsStream.ReadByte();
233: int magic2 = bsStream.ReadByte();
242: int magic3 = bsStream.ReadByte();
378: thech = (char)bsStream.ReadByte();
649: thech = (char)bsStream.ReadByte();
717: thech = (char)bsStream.ReadByte();
814: thech = (char)bsStream.ReadByte();
9: private const int BufferSize = 128 * 1024;
14: private readonly byte[] _buffer = new byte[BufferSize];
368: _data.avail_in = _stream.Read(_buffer, 0, ufferSize);
SharpZipLib und SharpCompress lesen bei Bedarf jeweils ein Byte mit "ReadByte". Daher scheint es nicht zu überlaufen, selbst wenn es am bzip2-Stream-Trennzeichen unterbrochen wird. Da der Variablenname "thech" (das Zeichen?) Häufig ist, kann etwas gemeinsam sein. (Ich sehe diesen Variablennamen nicht in libbz2)
AR.Compression.BZip2 liest mit fester Länge in den Puffer. Da es nicht von selbst erweitert wird, ist es möglicherweise nicht möglich, es in Byteeinheiten zu lesen. Selbst wenn die Verarbeitung für jeden bzip2-Stream getrennt ist, wird sie überlaufen, sodass einige Maßnahmen erforderlich sind.
Das byteweise Lesen ist in Bezug auf die Position des Basisstroms genau, jedoch in Bezug auf die Verarbeitungsgeschwindigkeit nachteilig.
Vergleichen Sie die Zeit, die zum Entpacken einer großen Datei benötigt wird.
Verwenden Sie die japanische Version der Wikipedia-Dump-Daten. Diese Datei hat eine Multi-Stream-Konfiguration.
Erweitern Sie den Stream nacheinander mit SharpZipLib und SharpCompress.
test1.fsx
#r "SharpZipLib.1.2.0/lib/net45/ICSharpCode.SharpZipLib.dll"
open System
open System.IO
open ICSharpCode.SharpZipLib.BZip2
let target = "jawiki-20200501-pages-articles-multistream.xml.bz2"
do
use fs = new FileStream(target, FileMode.Open)
let buffer = Array.zeroCreate<byte>(1024 * 1024)
let mutable streams, bytes = 0, 0L
while fs.Position < fs.Length do
use bz = new BZip2InputStream(fs, IsStreamOwner = false)
let mutable len = 1
while len > 0 do
len <- bz.Read(buffer, 0, buffer.Length)
bytes <- bytes + int64 len
streams <- streams + 1
Console.WriteLine("streams: {0:#,0}, bytes: {1:#,0}", streams, bytes)
test2.fsx
#r "SharpCompress.dll"
open System
open System.IO
open SharpCompress.Compressors
let target = "jawiki-20200501-pages-articles-multistream.xml.bz2"
do
let mutable ignore = true
use fs = { new FileStream(target, FileMode.Open) with
override __.Dispose disposing = if not ignore then base.Dispose disposing }
let buffer = Array.zeroCreate<byte>(1024 * 1024)
let mutable streams, bytes = 0, 0L
while fs.Position < fs.Length do
use bz = new BZip2.BZip2Stream(fs, CompressionMode.Decompress, false)
let mutable len = 1
while len > 0 do
len <- bz.Read(buffer, 0, buffer.Length)
bytes <- bytes + int64 len
streams <- streams + 1
ignore <- false
Console.WriteLine("streams: {0:#,0}, bytes: {1:#,0}", streams, bytes)
Ausführungsergebnis
$ time ./test1.exe # SharpZipLib
streams: 24,957, bytes: 13,023,068,290
real 16m2.849s
$ time ./test2.exe # SharpCompress
streams: 24,957, bytes: 13,023,068,290
real 18m26.520s
SharpZipLib scheint schneller zu sein.
Extrahieren Sie alle Streams gleichzeitig mit SharpCompress und AR.Compression.BZip2.
test3.fsx
#r "SharpCompress.dll"
open System
open System.IO
open SharpCompress.Compressors
let target = "jawiki-20200501-pages-articles-multistream.xml.bz2"
do
use fs = new FileStream(target, FileMode.Open)
use bz = new BZip2.BZip2Stream(fs, CompressionMode.Decompress, true)
let buffer = Array.zeroCreate<byte>(1024 * 1024)
let mutable bytes, len = 0L, 1
while len > 0 do
len <- bz.Read(buffer, 0, buffer.Length)
bytes <- bytes + int64 len
Console.WriteLine("bytes: {0:#,0}", bytes)
test4.fsx
#r "AR.Compression.BZip2.dll"
open System
open System.IO
open System.IO.Compression
let target = "jawiki-20200501-pages-articles-multistream.xml.bz2"
do
use fs = new FileStream(target, FileMode.Open)
use bz = new BZip2Stream(fs, CompressionMode.Decompress, false)
let buffer = Array.zeroCreate<byte>(1024 * 1024)
let mutable bytes, len = 0L, 1
while len > 0 do
len <- bz.Read(buffer, 0, buffer.Length)
bytes <- bytes + int64 len
Console.WriteLine("bytes: {0:#,0}", bytes)
Ausführungsergebnis
$ time ./test3.exe # SharpCompress
bytes: 13,023,068,290
real 17m36.925s
$ time ./test4.exe # AR.Compression.BZip2
bytes: 13,023,068,290
real 8m23.916s
AR.Compression.BZip2 ist schnell, da es die native Bibliothek aufruft.
Python
Vergleiche mit Pythons bz2-Modul. Dies ist auch ein nativer Wrapper.
[Referenz] Multi-Stream bzip2 nacheinander mit Python erweitern
test5.py (sequentiell)
import bz2
target = "jawiki-20200501-pages-articles-multistream.xml.bz2"
streams = 0
bytes = 0
size = 1024 * 1024 # 1MB
with open(target, "rb") as f:
decompressor = bz2.BZ2Decompressor()
data = b''
while data or (data := f.read(size)):
bytes += len(decompressor.decompress(data))
data = decompressor.unused_data
if decompressor.eof:
decompressor = bz2.BZ2Decompressor()
streams += 1
print(f"streams: {streams:,}, bytes: {bytes:,}")
test6.py (kollektiv)
import bz2
target = "jawiki-20200501-pages-articles-multistream.xml.bz2"
bytes = 0
size = 1024 * 1024 # 1MB
with bz2.open(target, "rb") as f:
while (data := f.read(size)):
bytes += len(data)
print(f"bytes: {bytes:,}")
Ausführungsergebnis
$ time py.exe test5.py #Sequentiell
streams: 24,957, bytes: 13,023,068,290
real 8m12.155s
$ time py.exe test6.py #Bulk
bytes: 13,023,068,290
real 8m1.476s
Der größte Teil der Verarbeitung wird von libbz2 ausgeführt, und der Rest der Verarbeitung ist gering und schnell.
bzcat
Es misst auch den Befehl WSL1 bzcat.
$ time bzcat jawiki-20200501-pages-articles-multistream.xml.bz2 > /dev/null
real 8m21.056s
user 8m5.563s
sys 0m15.422s
Fassen Sie die Ergebnisse zusammen. Fügen Sie das Messergebnis von WSL1 (Mono) hinzu. Die Geschwindigkeit von Python ist ein Blickfang.
Sequentiell(Win) | Sequentiell(WSL1) | Bulk(Win) | Bulk(WSL1) | |
---|---|---|---|---|
SharpZipLib | 16m02.849s | 22m49.375s | ||
SharpCompress | 18m26.520s | 23m56.694s | 17m36.925s | 22m54.247s |
AR.Compression.BZip2 | 8m23.916s | 8m36.495s | ||
Python (bz2) | 8m12.155s | 8m45.590s | 8m01.476s | 8m28.749s |
bzcat | 8m21.056s |
In verwaltetem Code implementierte Bibliotheken dauerten mehr als doppelt so lange. Wenn Sie die Bindung nicht verwaltet haben, ist es sicherer, AR.Compression.BZip2 zu verwenden.
DeflateStream, der von System.IO.Compression.GZipStream verwendet wird, scheint von seiner eigenen Implementierung zu einem nativen Wrapper gewechselt zu sein.
Ab .NET Framework 4.5 verwendet die DeflateStream-Klasse die zlib-Bibliothek zur Komprimierung.
In den folgenden Artikeln finden Sie Wikipedia-Dumps.
Dieser Artikel befasst sich mit bzip2 in SharpZipLib.
Ein Artikel, in dem SharpCompress erwähnt wird.