Performancetest von Datenträgern mit Flexible IO messen

Meine bisherigen Messungen der Performance habe ich immer mit sysbench durchgeführt. Mit Flexible IO (fio) gibt es aber ein Stück Software, das sehr viel umfangreichere Tests durchführen kann

stilisiertes FIO Logo
raspithek.de - Olli Graf
fio_logoCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
stilisiertes FIO Logo

fio

fio ist in C geschrieben und steht unter GPL V2 Lizenz. Es ist für Linux, Mac OS und Windows verfügbar.

Installation

In den Repositories von Armbian und Raspberry Pi OS ist fio bereits enthalten und kann daher einfach mit

sudo apt install fio -y

installiert werden. Auch die zum Hauptprogramm gehörigen Tools sind darin enthalten.

Falls nötig, kannst du den Quellcode mit

git clone https://git.kernel.dk/cgit/fio/

Vom git Repository herunterladen und selber bauen.

Der erste Test

Ich verwende in diesem Post den Orange Pi 5 Pro, da er mit einer NVME und einem eMMC Modul ausgestattet ist.

Unser erster Testaufruf sieht so aus:

fio --name=random-write --ioengine=posixaio --rw=randwrite --bs=4k --numjobs=1 --size=4g --iodepth=1 --runtime=60 --time_based --end_fsync=1

Das sieht kompliziert aus, wenn man es erstmal verstanden hat, bemerkt man aber, welche Möglichkeiten fio hat. Außerdem werden wir den Aufruf weiter unten noch vereinfachen.

--name=random-write
Dieser Parameter definiert einen frei wählbaren Namen für den Test.

--ioengine=posixaio
Hiermit wird die verwendete I/O Engine festgelegt. posixaio steht für Posix Async I/O. Unter Windows benutzt du besser windowsaio. Der Mac unterstützt posixaio nur eingeschränkt, vielleicht ist da psync besser geeignet.

--rw=randwrite
Jetzt legen wir den Testtyp fest: Mit randwrite werden zufällige Schreibzugriffe getestet. Weitere Werte sind read, write, randread, randrw

--bs=4k
Damit definieren wir die Blockgröße (block size) für die I/O Operationen und legen sie auf 4KB fest. Alternativ ist auch eine Blockgröße von 8k möglich, aber die meisten Dateisysteme arbeiten mit einer Blockgröße von 4KB.

--numjobs=1
Wir setzen die Anzahl der parallelen Threads, die verwendet werden sollen auf 1. Mit einem Wert von 4 oder 8 kannst du gut parallele Zugriffe testen.

--size=4g
Die Datenmenge, die pro Job geschrieben werden kann, legen wir auf 4GB fest.

--iodepth=1
Dieser Parameter legt die Anzahl der gleichzeitig laufenden I/O-Anfragen pro Job fest. Mit einem Wert von 1 wartet jeder Job mit einer neuen Anfrage so lang, bis die vorherige abgeschlossen ist.

--runtime=60
Dieser Test soll 60s lang laufen. Dieser Parameter „beißt“ sich etwas mit --size, da der Test evtl. nicht lang genug läuft, bis die definierte Datenmenge voll ausgeschöpft wurde. Mit –runtime lassen sich sehr gut Langzeittest initiieren.

--time_based
Hiermit legen wir fest, dass der Test zeitbasiert beendet werden soll, sonst würde er laufen, bis die Datenmenge aus –size vollständig verwendet wurde.

--end_fsync=1
Der letzte Parameter bewirkt, dass zum Abschluss des Tests fsync() aufgerufen wird, um sicher zu stellen, dass alle Daten wirklich ins Dateisystem geschrieben werden. Dadurch steigt der Realitätsbezug des Tests.

Wenn der Test dann vollständig durchgelaufen ist, gibt er das Resultat auf der Console aus.

Ergebnisprotokoll des Testdurchlaufs auf der NVME
raspithek.de - Olli Graf
fio-first-testCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Ergebnisprotokoll des Testdurchlaufs auf der NVME

Das Ergebnis interpretieren

Die Menge an Informationen, die fio ausgibt, kann einen schon erschlagen, aber schauen wir uns die wichtigsten Ergebnisse mal im Detail an:

IOPS=29.8k
Es wurden 29800 Schreibvorgänge pro Sekunde durchgeführt, mit lediglich iodepth=1 ist das ein gutes Ergebnis.

BW=116MiB/s (122MB/s)
Dies ist der Schreibdurchsatz, einmal mit in 116 Mebibytes per second und in 122 Megabyte pro Sekunde. Mebibytes ist der in der Digitaltechnik übliche Binärpräfix mit Basis 1024, Megabytes orientiert sich an der Basis 1000, die im SI-Einheitensystem üblich ist. Aus Marketinggründen steht häufig der höhere Wert auf der Packung.

lat (usec): min=10, max=10781, avg=26.12, stdev=15.04
In dieser Zeile werden die Latenzen angeben, Als minimaler, maximaler Wert, sowie Durchschnitt und Standardabweichung. Daraus ergibt sich, dass 99.9% der Schreibzugriffe unter 81 µs lagen. Der Ausreißer von max=10,7ms ist vermutlich auf ein kurzes Thermal Throtteling der NVME zurückzuführen. Dies wirkt sich natürlich negativ auf die Standardabweichung aus. Je kleiner dieser Wert ist, desto gleichmäßiger war die Performance während des Tests.

iops : min=18334, max=60766, avg=35487.98, stdev=10256.14, samples=106

Ähnlich der Latenzzeile werden hier die Statistikwerte für die Schreibvorgänge pro Messintervall anzeigt. Der Wert samples=106 deutet darauf hin, dass alle 0,5s eine Messung durchgeführt wurde.

Testdatei

Nach dem Test steht eint 4GB große Datei namens random-write.0.0 im aktuellen Verzeichnis. Diese kann gelöscht werden, sie ist nicht weiter wichtig, bei einem weiteren Testlauf würde eine Datei random-write-1.0 erzeugt werden.

Die Testdatei random-write.0.0 ist 4GB groß, wie  mit --size=4G konfiguriert.
raspithek.de - Olli Graf
test-fileCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Die Testdatei random-write.0.0 ist 4GB groß, wie mit –size=4G konfiguriert.

Logfiles

fio kann alle Ergebnisse auch in Logdateien speichern. Dazu gibt es mehrere Programmoptionen:

  • --write_bw_log=mytest – schreibt die Durchsatzergebnisse in das Logfile mytest_bw.1.log
  • --write_lat_log=mytest – schreibt die Latenzergebnisse in die Logfiles mytest_lat.1.log,mytest_clat.1.log und mytest_slat.1.log
  • --write_iops_log=mytest – schreibt die IOPS-Ergebnisse in das Logfile mytest_iops.1.log

Wenn ich den obigen Lauf wiederhole, sieht das mit allen drei gesetzten Optionen so aus:

Die Logfiles im Dateisystem
raspithek.de - Olli Graf
fio-logsCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Die Logfiles im Dateisystem

In den Logfiles werden die Werte pro Messintervall zeilenweise festgehalten

grafische Auswertung

Die Daten der Logfiles können auch visualisiert werden. dazu wird das Programm fio_generate_plots mit installiert. Damit es arbeiten kann, musste ich in Armbian noch gnuplot nachinstallieren.

sudo apt install gnuplot

Um ein paar Messdaten mehr zu bekommen, mache ich einen längeren Testlauf

fio --name=test --rw=read --size=1G --time_based --runtime=60s --group_reporting     --log_avg_msec=100 --write_bw_log=mytest --write_lat_log=mytest --write_iops_log=mytest

Dieser reine Lesetest läuft 60s und erzeugt alle o.a. Logfiles. Der Parameter --group_reporting sorgt dafür, dass die Ergebnisse mehrere Jobs zu einer Gruppe zusammengefasst werden und nicht jeder Job einzeln behandelt wird. Das ist in diesem Beispiel nicht ganz so wichtig, ich wollte es nur mal erwähnt haben.

Dann kann ich mit

fio_generate_plots mytest

Die Charts zu jedem Log erzeugen lassen, fio_generate_plots sucht sich im aktuellen Verzeichnis selbstständig die Logdateien zusammen und erzeugt aus deren Inhalt die dazu gehörenden Graphen als SVG-Grafik. Diese konvertiere ich im Anschluss in ein PNG Bild und erhalte für die Durchsatz diese Kennlinie

Graph der Bandbreitenmessung
raspithek.de - Olli Graf
mytest-bwCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Graph der Bandbreitenmessung

Man erkennt, dass der Durchsatz immer wieder kurz rapide absinkt. Dies liegt vermutlich daran, dass die NVME unter der Platine des Orange Pi im Gehäuse sitzt, wo der Luftstrom des Lüfters nicht gut kühlen kann. Dadurch kommt es immer kurz zum Thermal Throtteling.

Jobfiles

Die Menge an Argumenten immer wieder einzutippen, ist natürlich lästig. Daher bietet fio die Möglichkeit, Jobfiles anzulegen, die alle Parameter für einen Testlauf definiert. Ich lege eine Datei fio_4k_read an und schreibe dies hinein:

#Lesetest mit 4K Blocksize, Laufzeit 60s
[4k_read]
rw=read
size=1G
blocksize=4k
time_based
runtime=60s
group_reporting
log_avg_msec=100
write_bw_log=4k_read
write_lat_log=4k_read
write_iops_log=4k_read

Der Test „4k_read“ ist ein reiner Lesetest rw=read, mit einer Dateigröße von 1GB size=1G und einem Blocksize blocksize=4k von 4k, er wird time_based für 60s runtime=60s durchgeführt. Das group_reporting ist eingeschaltet und die Logfiles werden unter dem Namen 4k_read abgelegt.

Ich kann jetzt den Testlauf einfach mit

fio fio_4k_read

starten und muss mir nicht jedes mal die korrekten Parameter überlegen.

Nach dem Testlauf des 4k_read Jobfiles sind die definierten Logfiles zu finden.
raspithek.de - Olli Graf
fio_4k_jobfileCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Nach dem Testlauf des 4k_read Jobfiles sind die definierten Logfiles zu finden.

Testsuite

Häufig ist es sinnvoll, auf einem Datenträger mehrere verschiedene Tests laufen zu lassen. Für diesen Zweck kannst du in einem Jobfile mehrere Testläufe definieren. Dazu kopiere ich fio_4k_read nah fio_suite und erweitere die Kopie um einen einen weiteren Testlauf für einen 1h Langzeittest.

#fio_suite: Testsuite mit Lesetest und 1h Langzeittest
[4k_read]
rw=read
size=1G
blocksize=4k
time_based
runtime=60s
group_reporting
log_avg_msec=100
write_bw_log=4k_read
write_lat_log=4k_read
write_iops_log=4k_read

[longrun]
ioengine=libaio
direct=1
time_based
runtime=1h
size=4G
numjobs=4
blocksize=4k
readwrite=randwrite
write_bw_log=longrun
write_lat_log=longrun
write_iops_log=longrun

Beide Tests werden mit dem Aufruf fio fio_suite hintereinander ab gespult und schreiben ihre Daten in separate Logfiles.

Unschön ist allerdings, dass Einstellungen wie blocksize oder time_based doppelt vorkommen.Auch dafür hat fio eine Lösung. Wir definieren zu Beginn eine [global] Sektion, in der alle Werte, die für alle Testläufe gelten sollen, gemeinsam deklariert werden.

#fio_suite: Testsuite mit Lesetest und 1h Langzeittest
[global]
ioengine=libaio
time_based
blocksize=4k

[4k_read]
rw=read
size=1G
runtime=60s
group_reporting
log_avg_msec=100
write_bw_log=4k_read
write_lat_log=4k_read
write_iops_log=4k_read

[longrun]
direct=1
runtime=1h
size=4G
numjobs=4
readwrite=randwrite
write_bw_log=longrun
write_lat_log=longrun
write_iops_log=longrun

den longrun Test lasse ich nochmal über die NVME laufen. Dabei zeigt sich, dass die Konstruktion der NVME auf der Unterseite im Asixsixx Gehäuse ungünstig ist. In den ersten Sekunden werden noch über 90.000 IOPS gemessen, kurz darauf riegelt der Controller aber wegen der Hitze auf irgendwas unterhalb von 10 IOPS ab. Vielleicht sollte ich doch noch einen Heatsink auf die NVME kleben.

raspithek.de - Olli Graf
longrun-iopsCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Die IOPS der NVME brechen durch die Wärmeentwicklung rasch zusammen.

Zum Abschluss lasse ich die Testsuite nochmal über das eMMC Modul laufen. Da direkt neben der NVME auf der Unterseite der Platine sitzt, zeigt sich ein ähnliches Bild.

Auch beim eMMC Modul sinken die IOPS Werte nach kurzer Zeit, liegen dann aber höher als bei der NVME.
raspithek.de - Olli Graf
eMMC-iopsCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Auch beim eMMC Modul sinken die IOPS Werte nach kurzer Zeit, liegen dann aber höher als bei der NVME.

Fazit

Der Name Flexible I/O verspricht nicht zu viel. fio ist ein Testprogramm mit vielen Möglichkeiten für den Test von Datenträgern. Deshalb ist der Blog-Post auch etwas länger als üblich geworden.

Schreibe einen Kommentar

Insert math as
Block
Inline
Additional settings
Formula color
Text color
#333333
Type math using LaTeX
Preview
\({}\)
Nothing to preview
Insert
Creative Commons License
Except where otherwise noted, the content on this site is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Olli Graf - raspithek.de
WordPress Cookie Hinweis von Real Cookie Banner