Dieser Beitrag wurde von einem LLM aus dem Englischen ins Deutsche übersetzt. Zum englischen Original
In manchen spezialisierten Anwendungen kann es wünschenswert oder sogar notwendig sein, ein unveränderliches Read-only-Dateisystem zu verwenden. Der Grund kann im darunterliegenden Speichermedium liegen, etwa einer Live-DVD, die sich nicht verändern lässt, oder einem Flash-Speicher in einem Embedded-System, der mit jedem Schreibzyklus altert. Ein unveränderliches Dateisystem bietet außerdem einen bekannten, wohldefinierten und stabilen Zustand. Einen Zustand, zu dem man mit einem Neustart jederzeit zurückkehren kann.
Ein Read-only-Dateisystem-Image funktioniert technisch eher wie ein Archiv mit wahlfreiem Zugriff, das einmal gepackt wird, zum Beispiel von einem Yocto-Build-Prozess. Ein Dateisystem, das explizit für diesen Anwendungsfall entworfen wurde, kann hochoptimierte Datenstrukturen und Kompressionstechniken bieten. Daten und Metadaten lassen sich vorsortieren, optimal anordnen, deduplizieren, und so weiter.
Auf Embedded-Linux-Systemen ist SquashFS derzeit das dominierende Dateisystem für solche Anwendungen. Es wurde 2009 in den Mainline-Linux-Kernel aufgenommen und löste das ältere cramfs ab. Anders als cramfs unterstützt SquashFS alle Annehmlichkeiten eines typischen Dateisystems, etwa
Das On-Disk-Format von SquashFS ist auf hohe Datendichte ausgelegt. Dateidaten werden in Blöcken von typischerweise 128KiB (bis zu 1MiB) komprimiert, wobei sich kleinere Dateien einen Block teilen.1 Metadaten (z.B. Inodes, Verzeichnisinhalte) werden ebenfalls komprimiert, in Blöcken von 8KiB. Dateidaten, Inodes und Verzeichnislisten werden getrennt voneinander abgelegt. Alle Datenstrukturen sind byteweise ausgerichtet, und SquashFS verwendet sogar Inodes variabler Größe, um noch die letzten Bytes herauszuholen.
Das Ergebnis ist ein sehr kompaktes Dateisystem-Image, allerdings auf Kosten der Lesegeschwindigkeit. Die Granularität der Metadatenblöcke bedeutet, dass beim Lesen eine gewisse Menge an Daten mitgeladen und entpackt wird, die gar nicht gebraucht wird. Da die komprimierten Blöcke variabel groß sind, liegen sie praktisch nie an den Blockgrenzen des Datenträgers, was einen gewissen Overhead bei der I/O verursacht. Unter Umständen ist vorab nicht einmal bekannt, wie viele Daten zu lesen sind. Zuerst muss ein Header gelesen werden, der dem Block variabler Größe vorausgeht.
2019 wurde EROFS vorgestellt, erstmals verfügbar in Kernel 5.4. EROFS ist ebenfalls ein komprimiertes Read-only-Dateisystem, priorisiert in seinem Design aber die Leseleistung.
Während SquashFS Daten mit einer festen Eingabeblockgröße packt und dabei komprimierte Blöcke variabler Größe erzeugt, verwendet EROFS Fixed Output Compression. Die Menge der Eingabedaten darf flexibel sein, aber die erzeugten komprimierten Blöcke haben eine feste Größe. Dadurch liegen sie immer ideal an den I/O-Grenzen des Datenträgers. Die Größe ist stets im Voraus bekannt, und die Blöcke lassen sich über einen Index statt über einen Offset adressieren.
In EROFS werden Daten und Metadaten außerdem verschränkt statt getrennt gespeichert, was in manchen Fällen bessere Lokalität und schnellere sequenzielle Lesezugriffe ermöglicht.
Um herauszufinden, wie gut sich EROFS im Vergleich zu SquashFS schlägt, haben wir einen Größenvergleich und einen Lesegeschwindigkeits-Benchmark durchgeführt.
Für diesen Benchmark wurde ein SquashFS-Image von der Debian 10.2 Xfce Live-DVD extrahiert und in verschiedene andere Formate konvertiert. Das Image von der DVD ist rund 2GiB groß und verwendet die Standard-XZ-Kompression mit der Standardblockgröße von 128KiB.
EROFS unterstützte ursprünglich nur lz4-Kompression, hat aber kürzlich experimentelle Unterstützung für lzma hinzugefügt. Das erfordert derzeit einen eigenen Build der ebenfalls experimentellen Alpha-Version 5.3.2 von xz-utils.
SquashFS unterstützt ebenfalls lz4-Kompression. Um beide zu vergleichen, wurde
das SquashFS-Image mit den tar-Konvertierungsprogrammen aus
squashfs-tools-ng umgewandelt:
sqfs2tar debian.sqfs | tar2sqfs -c <compressor> test.sqfs
Als Referenz wurden auf diesem Weg auch die Tarballs mit derselben Kompression
erzeugt, und tar2sqfs wurde zusätzlich so angepasst, dass es ein vollständig
unkomprimiertes SquashFS-Image erzeugt.
Für einen besseren Größenvergleich und um den Effekt der Fixed Output Compression zu sehen, wurden weitere SquashFS-Images erzeugt, bei denen die Blockgröße von 128KiB auf 4KiB reduziert wurde, die Standard-Ausgabeblockgröße von EROFS.
Die EROFS-Images wurden gebaut, indem das SquashFS-Image entpackt und dann mit
mkfs.erofs aus den erofs-utils neu gepackt wurde:
fakeroot rdsquashfs -XTCO -u / -p temp/ test.sqfs
fakeroot mkfs.erofs -z<compressor> test.erofs temp/
Sehen wir uns zuerst die absoluten Imagegrößen an.
| Kompressor | tar | SquashFS | SquashFS 4KiB | EROFS |
|---|---|---|---|---|
| none | 6.5 GiB | 6.1 GiB | 6.1 GiB | 6.4 GiB |
| LZ4 | 3.3 GiB | 3.1 GiB | 3.6 GiB | 3.9 GiB |
| LZ4 HC | 2.8 GiB | 2.7 GiB | 3.4 GiB | 3.6 GiB |
| XZ | 1.7 GiB | 2.0 GiB | 2.6 GiB | 2.7 GiB |
Wir sehen sofort, dass SquashFS mit Standardeinstellungen kleinere Images erzeugt als EROFS. Reduziert man die Blockgröße auf 4KiB, leidet die SquashFS-Kompression deutlich unter dem kleineren Fenster, und die Ergebnisse nähern sich denen von EROFS an, bleiben aber kleiner.
Im unkomprimierten Fall liegen SquashFS und EROFS nahe beieinander, sind aber
etwas kleiner als der Referenz-Tarball. Beide verwenden kompaktere, binäre
Datenstrukturen als tar und unterstützen Deduplizierung.
EROFS hat zudem etwas mehr Overhead als SquashFS, weil es größere, aber leichter zu dekodierende Datenstrukturen verwendet. Außerdem legt es mehr Wert auf Datenausrichtung für schnelle Lesezugriffe.
Bei LZ4 schlägt SquashFS sogar den komprimierten Tarball, und erst im XZ-Fall gewinnt die Stream-Kompression mit ihrem großen gleitenden Fenster schließlich gegen die feste Blockgröße von 128KiB.
Das XZ-komprimierte EROFS liegt größenmäßig ziemlich nahe bei SquashFS mit 4KiB-Blockgröße. Das ist eine Folge der Fixed Output Compression in EROFS. Es wird deutlicher, wenn wir jedes Format relativ zu seiner eigenen unkomprimierten Basis betrachten, was uns einen besseren Einblick gibt, wie effektiv jedes Format die Datenkompression nutzt:
| Kompressor | tar | SquashFS | SquashFS 4KiB | EROFS |
|---|---|---|---|---|
| none | 100.0% | 100.0% | 100.0% | 100.0% |
| LZ4 | 50.6% | 51.7% | 59.9% | 61.7% |
| LZ4 HC | 42.5% | 44.1% | 56.1% | 57.0% |
| XZ | 25.3% | 32.1% | 43.3% | 42.7% |
Mit effektiveren Kompressoren schrumpft der Abstand zwischen SquashFS mit 4KiB-Blockgröße und EROFS. Das liegt daran, dass EROFS bei besserer Kompression das Eingabefenster vergrößern kann, was die Kompressionsrate verbessert.
Für einen Geschwindigkeitsvergleich wurden ein kleiner Linux-5.19-Kernel und eine busybox-basierte initrd für einen Raspberry Pi 3 gebaut. Das zu testende Dateisystem lag auf einer separaten ext4-Partition auf der Boot-Micro-SD-Karte. Aus der busybox-Shell wurde das Image dann per Loop-Device gemountet und ein Teil seines Inhalts in einen Tarball gepackt, was eine Baumtraversierung und Zugriffe auf eine Reihe von Dateien auslöst:
mkdir -p /mnt/sdcard/ /mnt/testfs/
mount /dev/mmcblk0p2 /mnt/sdcard/
mount /mnt/sdcard/<testfs> /mnt/testfs/
time /bin/tar cf - /mnt/testfs/usr/bin > /dev/null
Wiederholt man die letzte Zeile 5 Mal (mit Leeren des Page-Cache dazwischen) und nimmt die schlechteste Zeit, ergibt sich folgende Tabelle:
| Kompressor | SquashFS | SquashFS 4KiB | EROFS |
|---|---|---|---|
| none | 6.75s | 7.60s | 6.38s |
| LZ4 | 4.76s | 6.61s | 4.05s |
| LZ4 HC | 4.40s | 6.23s | 3.59s |
| XZ | 24.81s | 32.44s | 17.71s |
Die I/O-Leistung lässt sich etwas leichter veranschaulichen und vergleichen, wenn wir die Größe des erzeugten Tarballs (~125.0 MiB) durch die benötigte Zeit teilen:
| Kompressor | SquashFS | SquashFS 4KiB | EROFS |
|---|---|---|---|
| none | 18.5 MiB/s | 16.5 MiB/s | 19.6 MiB/s |
| LZ4 | 26.3 MiB/s | 18.9 MiB/s | 30.9 MiB/s |
| LZ4 HC | 28.4 MiB/s | 20.1 MiB/s | 34.8 MiB/s |
| XZ | 5.0 MiB/s | 3.9 MiB/s | 7.1 MiB/s |
Der Wechsel von unkomprimiert zu LZ4 beschleunigt den Vorgang in allen 3 Fällen. Eine höhere Datendichte durch den HC-Modus bringt einen weiteren Geschwindigkeitsgewinn. Die Zeit, die durch das Lesen von weniger Daten von der SD-Karte gespart wird, überwiegt die Zeit, die das Wiederherstellen aus der komprimierten Form kostet. Wir erkennen, dass die LZ4-Dekompression in unserem Testaufbau I/O-gebunden ist.
Mit LZMA-Kompression bricht die Lesegeschwindigkeit dagegen drastisch ein, verglichen mit dem Lesen unkomprimierter Daten. Das Entpacken der Daten auf der CPU ist in allen 3 Fällen der Flaschenhals.
Vergleicht man EROFS zuerst mit SquashFS bei einer Blockgröße von 4KiB, profitiert EROFS von den ausgerichteten Lesezugriffen und übertrifft die Leseleistung unabhängig vom verwendeten Kompressor. Der Vorsprung von EROFS gegenüber SquashFS wächst sogar mit steigender Kompressionsrate.
Das lässt sich wieder mit der Fixed Output Compression und der Ausrichtung erklären. Bei SquashFS liegen die Dateisystemblöcke fast nie an den Grenzen der Geräteblöcke. Beim Lesen eines Blocks entsteht also ein gewisser Overhead an Daten, die zwar übertragen werden, aber eigentlich zu einem anderen Block gehören. Die I/O-Transfers von der SD-Karte werden ineffizient genutzt. Bei EROFS ist das nicht der Fall, und die Fixed Output Compression bedeutet, dass eine höhere Datendichte tatsächlich die Ausnutzung der gelesenen Geräteblöcke verbessert.
Ebenso sehen wir, dass SquashFS mit größerer Blockgröße an Leistung gewinnt. Deutlich mehr Dateien kleiner als 128KiB werden nun in gemeinsame “Fragment-Blöcke” gepackt. SquashFS hält intern einen Cache von 3 Fragment-Blöcken, und mit einer größeren Blockgröße erzielen wir nun mehr Cache-Treffer. Bei jedem Cache-Miss muss zwar ein viel größeres Stück von der SD-Karte gelesen werden, aber der Overhead durch unausgerichtete Lesezugriffe ist nun vergleichsweise klein.
Ist EROFS also der bessere Ersatz für SquashFS? Es kommt darauf an.
Datenkompression ist ein klassisches Beispiel für einen Kompromiss zwischen Speicherplatz und Zeit. Selbst in unserem eigenen Benchmark sehen wir zum Beispiel, dass LZMA fast 60% Platzersparnis bringt, dafür aber beim Entpacken der Daten Leistung kostet. LZ4 ist im Vergleich extrem schnell, kommt aber nicht annähernd an diese Datendichte heran.
Genauso ist SquashFS darauf ausgelegt, das letzte Byte aus einem Dateisystem herauszuholen. Das Ziel von EROFS ist dagegen, trotz Kompression schnell zu sein; dafür opfert es Platz zugunsten von Geschwindigkeit.
Unserem Benchmark zufolge erreichen beide Dateisysteme ihre jeweiligen Ziele recht gut.
Wenn Speicherplatz knapp ist, spricht vieles dafür, bei SquashFS zu bleiben. Wenn Datei-I/O in Ihrer Anwendung ein Flaschenhals ist, lohnt sich ein Blick auf EROFS auf jeden Fall.
Bitte beachten Sie, dass die LZMA-Kompression für EROFS zum Zeitpunkt der Erstellung dieses Artikels noch als experimentell gilt.
Auf EROFS-Seite wäre Zstd ein interessanter Kompressor als Ergänzung. Wie andere SquashFS-Benchmarks gezeigt haben, bringt Zstd gegenüber LZ4 deutliche Gewinne bei der Datendichte, lässt sich aber immer noch viel schneller entpacken als LZMA.2
Veröffentlicht am
21.07.2022
Kategorie
embedded
Autoren
David Oberhollenzer
sigma star gmbh
Eduard-Bodem-Gasse 6, 1. Stock
6020 Innsbruck | Österreich