Reading and writing data from mass storage (volumes) is a quite common pattern in software. However, some time elapses between starting the I/O request (i.e., reading or writing data) and the completion of the request. The elapsed time to complete the request is called I/O latency. Older magnetic hard disks need some time to position the head on the right track of the magnetic disk. Also, newer flash-based disks like SSDs need some time to read the data. In addition, at common cloud providers, different types of block volumes are available that provide a different I/O latency. With the software ioping, this latency can be measured.
Having a good understanding of the I/O latency of the used mass storage device is crucial for implementing fast / low latency software systems. For example, in a database management system, multiple I/O requests are usually needed to execute a query. Monitoring the I/O latency can also be useful to detect a defect on a storage device (e.g., bad sectors on a hard disk) that lead to higher processing times of an executed software system.
The ioping software can determine the I/O latency of a device. Like with the regular ping
command, the delay of requests is measured.
The ioping
package is included in most modern distributions. On Debian-based distributions, the software can be installed as follows:
Installing and Executing ioping
$ sudo apt install ioping
The software provides a wide range of options. Required is only the destination folder in which the I/O requests are performed. In addition, the parameter -c
allows specifying how many requests should be performed.
In the following example, 10 I/O requests are executed in the directory /tmp
. Per default, an I/O request of 4 kilobytes is executed, other sizes can be specified by using the -s
parameter. With the parameter -i
the delay between two requests can be specified. Per default, a delay of one second is used.
$ ioping -c 10 /tmp
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=1 time=423.2 us (warmup)
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=2 time=636.9 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=3 time=629.6 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=4 time=612.6 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=5 time=599.9 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=6 time=590.8 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=7 time=612.9 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=8 time=638.5 us (slow)
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=9 time=608.0 us
4 KiB <<< /tmp (ext4 /dev/vda1 39.3 GiB): request=10 time=685.0 us (slow)
--- /tmp (ext4 /dev/vda1 39.3 GiB) ioping statistics ---
9 requests completed in 5.61 ms, 36 KiB read, 1.60 k iops, 6.26 MiB/s
generated 10 requests in 9.00 s, 40 KiB, 1 iops, 4.44 KiB/s
min/avg/max/mdev = 590.8 us / 623.8 us / 685.0 us / 26.5 us
In the summary of the executed command about can be seen that the used system has an average I/O latency of 623.8 us
.
To get an output that is more suitable for post-processing, the option -print-count <n>
can be used. After n
requests, raw statistics are printed. With the option -quiet
, the normal output can be suppressed. So, to get a good output that can be used for further processing, the options -print-count 1 -quiet
can be used. For example:
$ ioping -print-count 1 -c 10 -quiet /tmp
1 580777 1722 7052621 580777 580777 580777 0 2 1000832419
1 633389 1579 6466800 633389 633389 633389 0 1 1000147838
1 591484 1691 6924955 591484 591484 591484 0 1 999936195
1 638930 1565 6410718 638930 638930 638930 0 1 1000043713
1 617406 1620 6634208 617406 617406 617406 0 1 999983362
1 598996 1669 6838109 598996 598996 598996 0 1 999990179
1 564540 1771 7255465 564540 564540 564540 0 1 999962737
1 602750 1659 6795521 602750 602750 602750 0 1 1000025151
1 643763 1553 6362590 643763 643763 643763 0 1 1000046368
The format of the raw statistics is as follows:
Column | Meaning | Remarks |
---|---|---|
1 | count of requests in statistics | |
2 | running time | ns |
3 | requests per second | iops |
4 | transfer speed | bytes / seconds |
5 | minimal request time | ns |
6 | average request time | ns |
7 | maximum request time | ns |
8 | request time standard | ns |
9 | total requests | including warmup, too slow or too fast |
10 | total running time | nanoseconds |
Comparing Volumes
To generate graphs of two different mass-storage devices, ioping
is executed in the following example in the AWS cloud. A t3a.small
instance is executed and a 100 GB gp2
and a 100 GB gp3
EBS volume are attached to the EC2 instance. According to some posts (see this and this) the newer gp3
EBS volume type might have a higher I/O latency than the older gp2
volume type. Let’s see if this can be confirmed by ioping
and a plot of the individual execution times.
To execute the following commands, both volumes are formatted with an EXT4 files system using the default parameters of mkfs.ext4
. The gp2
volume is mounted to the mount point /mnt/gp2
and the gp3 volume is mounted to the mount point /dev/gp3
. To compare the latency, for each of the mount points, ioping
is executed.
GP2 volume
sudo ioping -c 100 -i 100ms /mnt/gp2
[...]
--- /mnt/gp2 (ext4 /dev/nvme1n1 97.9 GiB) ioping statistics ---
99 requests completed in 36.8 ms, 396 KiB read, 2.69 k iops, 10.5 MiB/s
generated 100 requests in 9.90 s, 400 KiB, 10 iops, 40.4 KiB/s
min/avg/max/mdev = 240.7 us / 371.9 us / 1.44 ms / 174.4 us
GP3 volume
sudo ioping -c 100 -i 100ms /mnt/gp3
[...]
--- /mnt/gp3 (ext4 /dev/nvme2n1 97.9 GiB) ioping statistics ---
99 requests completed in 52.2 ms, 396 KiB read, 1.90 k iops, 7.41 MiB/s
generated 100 requests in 9.90 s, 400 KiB, 10 iops, 40.4 KiB/s
min/avg/max/mdev = 246.9 us / 527.5 us / 1.20 ms / 207.2 us
It can be seen in the output of the commands that the gp2
volumes have an average latency of 371.9 us; the gp3
volume has an average latency of 527.5 us
.
Generate Graphs
Gnuplot is a tool that can be used to plot and visualize data. To generate the raw data for the visualization, the following commands can be executed.
sudo ioping -c 1000 -i 100ms -print-count 1 -quiet /mnt/gp2 > gp2.out
sudo ioping -c 1000 -i 100ms -print-count 1 -quiet /mnt/gp3 > gp3.out
After these commands are executed, the files gp2.out
and gp3.out
with the ioping statistics are created. These files can be processed directly by gnuplot using the following template:
set autoscale
set grid x y
set ylabel "I/O latency (us)"
set xlabel "I/O request number"
set term svg
set output "/dev/null"
set title "EBS GP2 volume attachted to a t3a.small EC2 instance"
plot 'gp2.out' using (column(0)):($6/1000)
min_y = GPVAL_DATA_Y_MIN
max_y = GPVAL_DATA_Y_MAX
f(x) = mean_y
fit f(x) 'gp2.out' using (column(0)):($6/1000) via mean_y
stddev_y = sqrt(FIT_WSSR / (FIT_NDF + 1 ))
set label 1 gprintf("Minimum = %g", min_y) at 20, 100
set label 2 gprintf("Average = %g", mean_y) at 20, 1650
set label 3 gprintf("Maximum = %g", max_y) at 20, 1720
set label 4 gprintf("Standard deviation = %g", stddev_y) at 20, 1790
set yrange [0:max_y+300]
set output "gp2.svg"
plot min_y with filledcurves y1=mean_y lt 1 lc rgb "#bbbbdd" title "< Average", \
max_y with filledcurves y1=mean_y lt 1 lc rgb "#bbddbb" title "> Average", \
'gp2.out' using (column(0)):($6/1000) pt 2 title "", \
mean_y lt 1 title "Average"
reset
set autoscale
set grid x y
set ylabel "I/O latency (us)"
set xlabel "I/O request number"
set term svg
set output "/dev/null"
set title "EBS GP3 volume attachted to a t3a.small EC2 instance"
plot 'gp3.out' using (column(0)):($6/1000)
min_y = GPVAL_DATA_Y_MIN
max_y = GPVAL_DATA_Y_MAX
f(x) = mean_y
fit f(x) 'gp3.out' using (column(0)):($6/1000) via mean_y
stddev_y = sqrt(FIT_WSSR / (FIT_NDF + 1 ))
set label 1 gprintf("Minimum = %g", min_y) at 20, 100
set label 2 gprintf("Average = %g", mean_y) at 20, 2700
set label 3 gprintf("Maximum = %g", max_y) at 20, 2800
set label 4 gprintf("Standard deviation = %g", stddev_y) at 20, 2900
set yrange [0:max_y+500]
set output "gp3.svg"
plot min_y with filledcurves y1=mean_y lt 1 lc rgb "#bbbbdd" title "< Average", \
max_y with filledcurves y1=mean_y lt 1 lc rgb "#bbddbb" title "> Average", \
'gp3.out' using (column(0)):($6/1000) pt 2 title "", \
mean_y lt 1 title "Average"
When this template is stored in the same directory as the statistics files with the name ioplot.plot
and the command gnuplot ioplot.plot
is executed, two SVG images are generated. These images contain a plot of the I/O latency along with the minimum, the average, and the maximum I/O latency.
The average execution I/O latency is roughly the same as shown in the initial commands (an average latency of 377 us for the gp2
volume type and 527 us for the gp3
volume type). The suspicion that the gp3
volumes have a higher latency could be proven by this execution. In addition, the standard deviation of the requests is higher (140 for the gp2
volume type and 229 for the gp3
volume type)
Summary
ioping
is a tool to measure the I/O latency of a volume. gnuplot
is a tool that can be used to plot and visualize data. It can be used to plot the raw statistics of iopoing
.