Learn how to benchmark disk I/O performance using fio (Flexible I/O tester) to identify storage bottlenecks and optimize your Linux system's disk performance through comprehensive read/write testing and analysis.
Prerequisites
- Root or sudo access
- At least 2GB free disk space for testing
- Basic understanding of Linux file systems
What this solves
Disk I/O performance is critical for application responsiveness and system throughput. The fio (Flexible I/O tester) tool provides comprehensive benchmarking capabilities to measure sequential and random read/write performance, identify storage bottlenecks, and validate storage optimizations. This tutorial shows you how to install fio, run standardized benchmarks, and interpret results to optimize your Linux system's disk performance.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest versions of all packages.
sudo apt update && sudo apt upgrade -y
Install fio benchmarking tool
Install fio and related system utilities for disk performance testing. The package includes the main fio binary and documentation.
sudo apt install -y fio iotop hdparm
Verify fio installation
Check that fio is properly installed and display version information to confirm functionality.
fio --version
which fio
Basic disk performance testing
Create test directory
Create a dedicated directory for benchmarking to isolate test files from system data. Use a location on the disk you want to test.
sudo mkdir -p /tmp/fio-test
cd /tmp/fio-test
Run sequential read performance test
Test sequential read performance with large block sizes to measure maximum throughput. This simulates reading large files or streaming data.
sudo fio --name=sequential-read --ioengine=libaio --rw=read --bs=1M --size=1G --numjobs=1 --time_based --runtime=30 --group_reporting
Run sequential write performance test
Test sequential write performance to measure sustained write throughput. This test creates a 1GB file with 1MB block sizes.
sudo fio --name=sequential-write --ioengine=libaio --rw=write --bs=1M --size=1G --numjobs=1 --time_based --runtime=30 --group_reporting --fsync=1
Run random read performance test
Test random read performance with small block sizes to simulate database or application workloads. This measures IOPS (Input/Output Operations Per Second).
sudo fio --name=random-read --ioengine=libaio --rw=randread --bs=4k --size=1G --numjobs=4 --time_based --runtime=30 --group_reporting --iodepth=32
Run random write performance test
Test random write performance with small blocks to measure worst-case write scenarios. This is critical for database and transactional workloads.
sudo fio --name=random-write --ioengine=libaio --rw=randwrite --bs=4k --size=1G --numjobs=4 --time_based --runtime=30 --group_reporting --iodepth=32 --fsync=1
Advanced mixed workload testing
Create comprehensive benchmark job file
Create a job file that runs multiple tests sequentially to provide a complete performance profile. This saves time and ensures consistent testing parameters.
[global]
ioengine=libaio
direct=1
size=1G
runtime=30
time_based=1
group_reporting=1
[seq-read]
rw=read
bs=1M
numjobs=1
stonewall
[seq-write]
rw=write
bs=1M
numjobs=1
fsync=1
stonewall
[rand-read-4k]
rw=randread
bs=4k
numjobs=4
iodepth=32
stonewall
[rand-write-4k]
rw=randwrite
bs=4k
numjobs=4
iodepth=32
fsync=1
stonewall
[mixed-workload]
rw=randrw
rwmixread=70
bs=4k
numjobs=4
iodepth=16
stonewall
Run comprehensive benchmark suite
Execute the complete benchmark suite and save results to a file for analysis. This provides a standardized performance baseline.
sudo fio /tmp/fio-test/comprehensive-test.fio --output=/tmp/fio-test/results.txt
sudo fio /tmp/fio-test/comprehensive-test.fio --output-format=json --output=/tmp/fio-test/results.json
Test specific device performance
Test a specific block device directly to bypass filesystem overhead. Replace /dev/sdb with your target device. This provides raw storage performance metrics.
sudo fio --name=device-test --ioengine=libaio --direct=1 --filename=/dev/sdb --rw=randread --bs=4k --size=100M --numjobs=1 --time_based --runtime=10
Interpreting benchmark results
Understanding key metrics
Learn how to interpret fio output to identify performance characteristics and bottlenecks. Focus on these critical measurements.
| Metric | Description | Good Values |
|---|---|---|
| Bandwidth (BW) | Throughput in MB/s or GB/s | Sequential: 100+ MB/s (HDD), 500+ MB/s (SSD) |
| IOPS | Input/Output Operations Per Second | Random 4k: 100+ (HDD), 10,000+ (SSD) |
| Latency | Average response time | Sequential: <10ms, Random: <1ms (SSD) |
| CPU usage | Processor utilization during I/O | <50% for I/O-bound workloads |
Compare results with storage type baselines
Use these baseline performance expectations to evaluate your results and identify potential issues.
| Storage Type | Sequential Read | Sequential Write | Random 4k Read | Random 4k Write |
|---|---|---|---|---|
| 7200 RPM HDD | 100-200 MB/s | 100-180 MB/s | 100-200 IOPS | 100-150 IOPS |
| SATA SSD | 500-550 MB/s | 450-520 MB/s | 10,000-20,000 IOPS | 8,000-15,000 IOPS |
| NVMe SSD | 2,000-7,000 MB/s | 1,500-6,000 MB/s | 50,000-500,000 IOPS | 40,000-300,000 IOPS |
| Enterprise NVMe | 3,500-7,000 MB/s | 3,000-6,500 MB/s | 100,000-1,000,000 IOPS | 80,000-800,000 IOPS |
Optimizing disk performance based on results
Check current I/O scheduler
Verify and optimize the I/O scheduler for your storage type. Different schedulers work better for different storage technologies.
cat /sys/block/sda/queue/scheduler
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler
Monitor real-time I/O activity
Use iotop to monitor disk activity during benchmarks and identify processes causing I/O contention. This helps correlate benchmark results with system behavior.
sudo iotop -o
sudo iostat -x 1 5
Clean up test files
Remove benchmark files to free up disk space after testing. Always clean up test data to avoid filling up storage.
sudo rm -rf /tmp/fio-test
sudo sync
Verify your setup
Confirm fio is working properly and you can run basic performance tests.
fio --version
sudo fio --name=quick-test --ioengine=libaio --rw=read --bs=4k --size=10M --numjobs=1 --runtime=5 --time_based
df -h /tmp
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Permission denied errors | Insufficient privileges for direct device access | Run fio with sudo or adjust file permissions |
| Very low performance on all tests | System under heavy load or failing storage | Check system load with top, test different times |
| Inconsistent results between runs | Background processes or caching effects | Run sync && echo 3 | sudo tee /proc/sys/vm/drop_caches before tests |
| fio command not found | Package not installed or PATH issue | Reinstall with package manager, check PATH |
| Tests consume too much disk space | Large test file sizes on small filesystems | Reduce size parameter or use --time_based option |
Next steps
- Optimize Linux filesystem performance with mount options and I/O schedulers
- Monitor system resources with Netdata real-time performance dashboard
- Configure Linux system performance monitoring with sar and sysstat
- Optimize Linux RAID configuration for maximum disk performance
- Benchmark database performance with sysbench and fio integration
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Global variables
TEST_DIR="/tmp/fio-test"
RESULTS_DIR="/tmp/fio-results"
COMPREHENSIVE_JOB_FILE="$TEST_DIR/comprehensive-test.fio"
# Print colored output
print_status() {
local color=$1
local message=$2
echo -e "${color}${message}${NC}"
}
# Usage message
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " -d, --test-dir DIR Test directory (default: /tmp/fio-test)"
echo " -r, --results-dir DIR Results directory (default: /tmp/fio-results)"
echo " -h, --help Show this help message"
exit 1
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-d|--test-dir)
TEST_DIR="$2"
shift 2
;;
-r|--results-dir)
RESULTS_DIR="$2"
shift 2
;;
-h|--help)
usage
;;
*)
print_status "$RED" "Unknown option: $1"
usage
;;
esac
done
# Cleanup function for error handling
cleanup() {
print_status "$YELLOW" "Cleaning up temporary files..."
rm -rf "$TEST_DIR" 2>/dev/null || true
}
# Set up error trap
trap cleanup ERR
# Check if running as root or with sudo
check_privileges() {
if [[ $EUID -ne 0 ]]; then
print_status "$RED" "This script must be run as root or with sudo"
exit 1
fi
}
# Detect distribution and package manager
detect_distro() {
print_status "$YELLOW" "[1/8] Detecting Linux distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
UPDATE_CMD="apt update && apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
UPDATE_CMD="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
UPDATE_CMD="yum update -y"
;;
*)
print_status "$RED" "Unsupported distribution: $ID"
exit 1
;;
esac
print_status "$GREEN" "Detected: $PRETTY_NAME (Package manager: $PKG_MGR)"
else
print_status "$RED" "Cannot detect Linux distribution"
exit 1
fi
}
# Update system packages
update_system() {
print_status "$YELLOW" "[2/8] Updating system packages..."
eval "$UPDATE_CMD"
print_status "$GREEN" "System packages updated successfully"
}
# Install fio and related tools
install_fio() {
print_status "$YELLOW" "[3/8] Installing fio and related tools..."
$PKG_INSTALL fio iotop hdparm
print_status "$GREEN" "fio and related tools installed successfully"
}
# Verify fio installation
verify_installation() {
print_status "$YELLOW" "[4/8] Verifying fio installation..."
if ! command -v fio &> /dev/null; then
print_status "$RED" "fio installation failed"
exit 1
fi
local fio_version=$(fio --version 2>/dev/null || echo "unknown")
local fio_path=$(which fio)
print_status "$GREEN" "fio installed successfully:"
echo " Version: $fio_version"
echo " Path: $fio_path"
}
# Create test directories
setup_test_environment() {
print_status "$YELLOW" "[5/8] Setting up test environment..."
# Create test and results directories
mkdir -p "$TEST_DIR"
mkdir -p "$RESULTS_DIR"
# Set proper permissions
chmod 755 "$TEST_DIR"
chmod 755 "$RESULTS_DIR"
print_status "$GREEN" "Test environment created:"
echo " Test directory: $TEST_DIR"
echo " Results directory: $RESULTS_DIR"
}
# Create comprehensive benchmark job file
create_job_file() {
print_status "$YELLOW" "[6/8] Creating comprehensive benchmark job file..."
cat > "$COMPREHENSIVE_JOB_FILE" << 'EOF'
[global]
ioengine=libaio
direct=1
size=1G
runtime=30
time_based=1
group_reporting=1
[seq-read]
rw=read
bs=1M
numjobs=1
stonewall
[seq-write]
rw=write
bs=1M
numjobs=1
fsync=1
stonewall
[rand-read-4k]
rw=randread
bs=4k
numjobs=4
iodepth=32
stonewall
[rand-write-4k]
rw=randwrite
bs=4k
numjobs=4
iodepth=32
fsync=1
stonewall
[mixed-workload]
rw=randrw
rwmixread=70
bs=4k
numjobs=4
iodepth=16
stonewall
EOF
chmod 644 "$COMPREHENSIVE_JOB_FILE"
print_status "$GREEN" "Comprehensive benchmark job file created: $COMPREHENSIVE_JOB_FILE"
}
# Run basic performance tests
run_basic_tests() {
print_status "$YELLOW" "[7/8] Running basic disk performance tests..."
cd "$TEST_DIR"
print_status "$YELLOW" "Running sequential read test..."
fio --name=sequential-read --ioengine=libaio --rw=read --bs=1M --size=1G \
--numjobs=1 --time_based --runtime=30 --group_reporting \
> "$RESULTS_DIR/sequential-read.txt" 2>&1
print_status "$YELLOW" "Running sequential write test..."
fio --name=sequential-write --ioengine=libaio --rw=write --bs=1M --size=1G \
--numjobs=1 --time_based --runtime=30 --group_reporting --fsync=1 \
> "$RESULTS_DIR/sequential-write.txt" 2>&1
print_status "$YELLOW" "Running random read test..."
fio --name=random-read --ioengine=libaio --rw=randread --bs=4k --size=1G \
--numjobs=4 --time_based --runtime=30 --group_reporting --iodepth=32 \
> "$RESULTS_DIR/random-read.txt" 2>&1
print_status "$YELLOW" "Running random write test..."
fio --name=random-write --ioengine=libaio --rw=randwrite --bs=4k --size=1G \
--numjobs=4 --time_based --runtime=30 --group_reporting --iodepth=32 --fsync=1 \
> "$RESULTS_DIR/random-write.txt" 2>&1
print_status "$GREEN" "Basic performance tests completed"
}
# Run comprehensive benchmark suite
run_comprehensive_tests() {
print_status "$YELLOW" "[8/8] Running comprehensive benchmark suite..."
cd "$TEST_DIR"
# Run comprehensive test with text output
fio "$COMPREHENSIVE_JOB_FILE" --output="$RESULTS_DIR/comprehensive-results.txt" 2>&1
# Run comprehensive test with JSON output
fio "$COMPREHENSIVE_JOB_FILE" --output-format=json --output="$RESULTS_DIR/comprehensive-results.json" 2>&1
# Set proper permissions for results
chmod 644 "$RESULTS_DIR"/*.txt "$RESULTS_DIR"/*.json
print_status "$GREEN" "Comprehensive benchmark suite completed"
}
# Verify results and display summary
verify_results() {
print_status "$YELLOW" "Verifying benchmark results..."
local results_count=$(find "$RESULTS_DIR" -name "*.txt" -o -name "*.json" | wc -l)
if [[ $results_count -gt 0 ]]; then
print_status "$GREEN" "✓ Benchmark results generated successfully"
echo ""
print_status "$GREEN" "Results location: $RESULTS_DIR"
echo "Available result files:"
ls -la "$RESULTS_DIR"
echo ""
print_status "$YELLOW" "Next steps:"
echo "1. Review results in: $RESULTS_DIR"
echo "2. Analyze performance bottlenecks"
echo "3. Run specific device tests if needed (see fio documentation)"
echo "4. Clean up test files when done: rm -rf $TEST_DIR"
else
print_status "$RED" "✗ No benchmark results found"
exit 1
fi
}
# Main execution
main() {
print_status "$GREEN" "=== FIO Disk Performance Benchmarking Setup ==="
check_privileges
detect_distro
update_system
install_fio
verify_installation
setup_test_environment
create_job_file
run_basic_tests
run_comprehensive_tests
verify_results
print_status "$GREEN" "=== FIO installation and benchmarking completed successfully ==="
}
# Execute main function
main "$@"
Review the script before running. Execute with: bash install.sh