fio - Flexible IO Tester

Bearbeiten

Neben dem reinen Benchmarking eignet sich fio auch zur Durchführung von spezifischen Stresstests zur Belastung der Hardware. Durch die Unterstützung diverser I/O-Engines wie libaio oder posixaio kann es sehr flexibel eingesetzt werden. Bei der Wahl einer dieser Engines wird die Art und Weise beeinflusst, in der die Ein- bzw. Ausgabe der Daten durchgeführt wird.

Der Ablauf von Benchmarks kann individuell über Kommandozeilenparameter oder Konfigurationsdateien definiert und angepasst werden. Der einfache und übersichtliche Aufbau dieser Konfigurationsdateien ermöglicht eine detaillierte und einfach zu reproduzierende Abbildung verschiedener Lastszenarien. Dabei können unter anderem die zeitliche Abfolge, die Datenmenge und die Anzahl gleichzeitiger Zugriffe (Jobs) konfiguriert werden. Somit können individuelle und reproduzierbare Benchmarks zur Analyse verschiedener Systemkonfigurationen erstellt werden.

Nachfolgend ist eine Konfigurationsdatei dargestellt, die alle Parameter enthält mit denen die Benchmarks in diesem Wikibook durchgeführt werden:

[global]
ioengine=libaio
bs=128k
directory=/ZFS/
refill_buffers
randrepeat=0
fallocate=none

[benchmark1]
name=benchmark1
rw=write
numjobs=10
size=100G

Im nächsten Listing ist ein komplexeres Testszenario abgebildet. Dabei werden drei Clients simuliert, die zeitversetzt und mit unterschiedlichem Verhalten Daten schreiben. Durch die Möglichkeit mehrere kleine Benchmarks innerhalb einer Konfigurationsdatei samt zeitlicher Abfolge zu definieren, können sehr komplexe Lastszenarien abgebildet werden.

mehr/weniger
[global]
ioengine=libaio
bs=128k
directory=/ZFS/
refill_buffers
randrepeat=0
fallocate=none

[client1]
startdelay=0
name=benchmark1
rw=write
numjobs=1
size=1000G

[client2]
startdelay=30
name=benchmark2
rw=write
numjobs=5
size=100G

[client3]
startdelay=60
name=benchmark3
rw=write
numjobs=10

Nachfolgend ist die Ausgabe der Benchmark-Ergebnisse zu sehen:

user@host:~$ fio --rw=write --name=benchmark1 --numjobs=1 --size=50M
test.file: (g=0): rw=write, bs=4K-4K/4K-4K/4K-4K, ioengine=sync, iodepth=1
fio-2.1.3
Starting 1 process

test.file: (groupid=0, jobs=1): err= 0: pid=305074: Thu Mar 31 09:04:37 2016
  write: io=51200KB, bw=219742KB/s, iops=54935, runt=   233msec
    clat (usec): min=13, max=87, avg=15.59, stdev= 2.74
     lat (usec): min=13, max=89, avg=16.16, stdev= 2.77
    clat percentiles (usec):
     |  1.00th=[   13],  5.00th=[   13], 10.00th=[   13], 20.00th=[   14],
     | 30.00th=[   14], 40.00th=[   14], 50.00th=[   14], 60.00th=[   14],
     | 70.00th=[   18], 80.00th=[   19], 90.00th=[   19], 95.00th=[   19],
     | 99.00th=[   25], 99.50th=[   26], 99.90th=[   27], 99.95th=[   29],
     | 99.99th=[   57]
    lat (usec) : 20=96.06%, 50=3.92%, 100=0.02%
  cpu          : usr=15.09%, sys=83.19%, ctx=4, majf=0, minf=25
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=0/w=12800/d=0, short=r=0/w=0/d=0

Run status group 0 (all jobs):
  WRITE: io=51200KB, aggrb=219742KB/s, minb=219742KB/s, maxb=219742KB/s, mint=233msec, maxt=233msec

Dem mittleren Block ab Zeile 6 können detaillierte Informationen zu dem ausgeführten Job, wie die durchschnittliche Schreibgeschwindigkeit, die erreichten IOPs und die Laufzeit entnommen werden. Wird der Benchmark mit mehreren parallelen Jobs durchgeführt, so wird dieser Block für jeden Job einzeln ausgegeben.

Die letzte Zeile der Ausgabe dient der Zusammenfassung aller Jobs und kann zur allgemeinen Auswertung des Benchmarks herangezogen werden. Die Parameter sind folgendermaßen zu interpretieren:

io geschriebene / gelesene Datenmenge
aggrb Gesamtgeschwindigkeit aller Jobs
mindb Geschwindigkeit des langsamsten Jobs
maxb Geschwindigkeit des schnellsten Jobs
mint Dauer des schnellsten Jobs
maxt Dauer des langsamsten Jobs

Alle für die durchzuführenden Benchmarks relevanten Kommandozeilenparameter und ihre Bedeutung können der nachfolgenden Tabelle entnommen werden:

--name Name des Tests und der anzulegenden Dateien
--ioengine Zu verwendende I/O-Engine
--rw Art des Tests (read/write/randread/randwrite)
--bs Blockgröße
--numjobs Anzahl paralleler Jobs
--size Dateigröße pro Job
--refill_buffers Schreibt immer neue Zufallsdaten in den Puffer
--randrepeat Seed für Zufallsgenerator immer gleich (wenn Wert 1 ist)
--sync Nutzt synchrone Schreibzugriffe (wenn Wert 1 ist)
--fallocate Reserviert den Speicher beim Anlegen der Dateien (wenn nicht none)

Auch fio konnte, wie bonnie++, durch das Schreiben eines einzelnen Datenstroms nicht die maximal mögliche Geschwindigkeit des Pools ausschöpfen. Dies liegt unter anderem daran, dass die sehr hohen Geschwindigkeiten sowohl die Benchmarktools als auch die gesamte I/O-Kette des Betriebssystems stark beanspruchen. Durch die Verwendung 10 paralleler Jobs konnte jedoch eine vollständige Auslastung des Pools erreicht werden. Da dieser aus 44 Festplatten besteht, über die alle Lese- bzw. Schreibzugriffe verteilt werden, können die 10 Datenströme näherungsweise als ein einzelner betrachtet werden. Die negativen Auswirkungen paralleler Zugriffe sind bei dieser Anzahl vernachlässigbar.

Für die Benchmarks im Rahmen dieses Wikibooks wird jeweils die in Linux integrierte libaio sowie eine Blockgröße von 128 KiB verwendet. Dies entspricht der Standardgröße der logischen Blöcke von ZFS. Um eventuelle Caching-Effekte zu vermeiden, wurde die zu schreibende bzw. zu lesende Datenmenge auf 1000 GiB (10 x 100 GiB) festgelegt. Dies entspricht der vierfachen Größe des Arbeitsspeichers. Die Parameter --refill_buffers und --randrepeat=0 sorgen dafür, dass nicht immer wieder die gleichen Daten geschrieben werden, um somit ein praxisnahes Ergebnis zu erzielen. Da ZFS die von fio verwendeten Methoden zur Reservierung des Speicherplatzes beim Anlegen von Dateien nicht unterstützt, sollte der Parameter --fallocate=none bei allen Benchmarks angegeben werden [4]. Andernfalls wird der gesamte angeforderte Speicherplatz zur Reservierung beschrieben, was bei großen Datenmengen viel Zeit in Anspruch nimmt.

Der standardmäßig bei der Untersuchung der verschiedenen Optimierungsmöglichkeiten durchgeführte Benchmark kann mit folgendem Befehl ausgelöst werden:

fio --name=benchmark --directory=/ZFS/ --ioengine=libaio --rw=write --bs=128k --numjobs=10 --size=100G --refill_buffers --randrepeat=0 --fallocate=none

Während die meisten Tests mit sequenziellen Lese- und Schreibzugriffen durchgeführt werden, wird es an einigen Stellen notwendig sein, zufällige Zugriffe zu simulieren. Dabei werden die Blöcke einer Datei nicht in der richtigen, sondern einer zufälligen Reihenfolge gelesen bzw. geschrieben, wodurch sich die Zugriffszeiten mechanischer Festplatten stark auswirken. Zur Durchführung eines solchen Benchmarks muss für den Parameter --rw entweder randread oder randwrite angegeben werden.

Bei der Untersuchung des Einflusses verschiedener Blockgrößen muss fio über den Parameter --bs an die jeweilige Größe angepasst werden. Um komprimierbare bzw. deduplizierbare Daten zu erzeugen, ist der Parameter --refill_buffers zu entfernen. Dabei wird der von fio genutzte Pufferinhalt zyklisch geschrieben, wodurch eine Datei entsteht, die entsprechend viele Redundanzen für eine Komprimierung bzw. Deduplizierung bietet. Sollten die beschriebenen Anpassungen notwendig sein, um spezifische Effekte sichtbar zu machen, werden diese an entsprechender Stelle kenntlich gemacht.

Siehe auch

Bearbeiten