A backend service can generate two storage workloads that move the same total number of bytes but perform very differently.
One workload reads a 4 KiB database index page, waits for the result, and then decides which page to read next. Another reads a 1 MiB region from a log file in one sequential request. Both eventually read 1 GiB, but the first workload issues hundreds of thousands of small dependent operations, while the second issues 1,024 large operations.
Calling one device “500 MB/s” does not predict how it will handle both workloads. Storage performance has several dimensions:
These measurements are connected. Changing request size or queue depth can improve one metric while making another worse.
A storage performance number is meaningful only when the workload behind it is known.
Before comparing devices, describe what the application asks storage to do. At minimum, a useful workload description includes:
Consider two read streams:
Workload A is a small random workload. Workload B is a large sequential workload. A device specification that reports only maximum sequential throughput says little about Workload A.
Application dependencies matter as well. A database index traversal may need the contents of one page before it knows the address of the next page. That dependency keeps queue depth low even if the device could process many independent requests concurrently.
By contrast, a storage engine scanning several independent files can submit work for all of them. The total byte count may be the same, but the device sees much more concurrency.
Latency is the elapsed time between starting an operation and observing its completion.
The exact start and end points must be stated. Device latency might be measured from command submission to device completion. Application-visible latency also includes software work before submission and after completion.
A useful decomposition is:
Queueing time is the time a request waits behind other work. Service time is the time spent actually being processed by the device. A device may have a short service time yet produce high total latency when its queue is overloaded.
Latency is normally reported in units such as:
1 ms = 0.001 s1 µs = 0.000001 sAn HDD random read often takes several milliseconds because it includes mechanical positioning. SSD latency is commonly measured in tens or hundreds of microseconds, depending on the device, request, and load.
Those unit differences are easy to underestimate:
A 10 ms operation is one hundred times as long as a 100 µs operation.
An average reports the total observed latency divided by the number of operations. It does not show whether a small fraction of requests were much slower.
Suppose ten operations have these latencies:
Most complete near 100 µs, but one takes 1,000 µs. The average is 190.5 µs, which describes neither the common case nor the slow request very well.
Latency-sensitive systems therefore examine percentiles:
For a request that fans out to many storage operations, tail latency matters especially. If a response must wait for every required operation, one slow operation can determine the response time.
Latency measured with one outstanding request is not the same as latency at saturation.
When arrival rate is comfortably below device capacity, a new request may start quickly. As the workload approaches the device's limit, requests accumulate. Throughput may stop increasing while queueing time continues to grow.
This is why maximum-throughput tests and low-latency tests answer different questions.
Throughput, sometimes called bandwidth, is the amount of data transferred per unit of time:
Storage throughput is commonly reported in MB/s or GB/s. Manufacturers usually use decimal units:
Some operating-system tools report binary units such as MiB/s:
The labels matter when comparing results. 500 MB/s is approximately 477 MiB/s; the byte rate did not change, only the unit.
Each request has some fixed work: creating and submitting a command, tracking it, processing its completion, and updating software state. A larger request spreads that fixed work across more bytes.
Compare transferring 1 MiB in two ways:
Both move the same bytes. The first form requires far more operations and may spend more time on per-request work. The second form also gives the device a long contiguous region to transfer.
Large sequential requests therefore tend to approach a device's advertised bandwidth. Small requests tend to be limited by operation rate or latency before they consume the full byte bandwidth.
The device is only one stage in a transfer. The interface, controller, kernel, CPU, memory system, and application must all keep up.
If an SSD can supply 7 GB/s but the path receiving its data can sustain only 4 GB/s, the observed throughput cannot exceed 4 GB/s. Performance is bounded by the slowest active stage.
A short transfer can also report misleadingly high throughput if it is served from a faster cache or ends before the device reaches a steady state. Sustained measurements over a sufficiently large dataset provide a more useful view of long-running workloads.
IOPS means input/output operations per second:
IOPS counts requests, not bytes. A result of 100,000 IOPS is incomplete unless the request size and operation type are known.
At a fixed request size, throughput and IOPS are connected:
For example, 100,000 operations per second with 4 KiB per operation move:
The same 100,000 IOPS with 64 KiB requests would imply 6.5536 GB/s. A SATA SSD cannot deliver that byte rate, so either its IOPS would fall, the request size would be smaller, or another limit would be reached.
If only one request can be outstanding, the next request cannot start until the current one finishes. Ignoring small gaps between operations:
A device completing one serial request every 10 ms can provide approximately:
One operation every 100 µs corresponds to:
This relationship explains why low-queue IOPS is fundamentally tied to latency. Advertised peak IOPS usually requires several requests to be active concurrently.
Queue depth, abbreviated QD, is the number of I/O operations submitted but not yet completed.
At queue depth one, the workload has at most one request in flight:
With one operation in flight, the timeline runs strictly in sequence: submit A, wait while A is outstanding, complete A, and only then submit B.
With four independent requests, the device can overlap work:
Four operations are outstanding at once, so the device can choose an efficient order and keep its internal parallelism busy.
On an HDD, multiple outstanding requests give the system an opportunity to choose an order that reduces head movement. The mechanism still has limited physical ability to serve random locations.
On an SSD, concurrency allows the controller to use multiple flash channels and dies. NVMe supports many queues so work from several CPUs can reach that parallel hardware without funneling through one small shared queue.
Queue depth may be reported per worker, per device, or for the entire benchmark. A result should identify which meaning it uses.
Suppose a request takes 200 µs from submission to completion. A single serial stream can complete at most about 5,000 operations per second:
If the device has independent internal resources, several outstanding requests may keep those resources busy and increase total IOPS. One request can be waiting on flash while another is being transferred or completed.
The steady-state relationship among concurrency, IOPS, and average latency is:
If a system completes 100,000 IOPS with an average latency of 200 µs:
This relationship is an application of Little's Law. It is useful as a consistency check: reported IOPS, latency, and queue depth should roughly agree when measured over the same steady-state interval.
Queue depth helps until the device has enough work to stay busy. Beyond that point, additional requests mostly wait.
Imagine a device saturated at 100,000 IOPS. Increasing queue depth from 32 to 128 may leave throughput near 100,000 IOPS while roughly quadrupling the number of requests waiting. Average and tail latency rise even though the headline IOPS does not.
For an offline batch job, maximizing throughput may justify a deep queue. For a request-response service, reserving some device capacity and keeping queues short may produce better tail latency.
Concurrency must also exist in the application. A dependent pointer chase cannot achieve high queue depth merely because the device supports it. Independent requests, batching, multiple workers, or an asynchronous I/O design are needed to create work that can overlap.
Loading simulation...
An access pattern is sequential when consecutive requests target nearby, increasing addresses. It is random when requests jump among distant locations with little predictable order.
The distinction affects HDDs and SSDs differently.
For an HDD, small random I/O repeatedly pays seek and rotational costs. A 7200-RPM platter completes 120 rotations per second, so one full rotation takes about 8.33 ms. A sector is equally likely to be anywhere in the rotation when a random request arrives, making the average rotational wait approximately half a rotation, or 4.17 ms.
Using a typical seek time, a simplified random-read service time might include:
At queue depth one, 12 ms per operation is roughly 83 IOPS. With 4 KiB requests, that is only about 0.34 MB/s of application data:
The same drive may stream hundreds of megabytes per second when reading a large sequential region. Once positioned, it can transfer many adjacent sectors without paying a full seek for each small piece.
This large gap is why request location and ordering are central to HDD performance.
An SSD does not move a mechanical head, so random access avoids the millisecond-scale seek penalty. Small random reads can complete orders of magnitude faster than on an HDD.
Sequential access still has advantages. Large requests reduce command overhead and make it easier to use flash parallelism efficiently. Small random writes can also create more mapping and garbage-collection work than a clean sequential write stream.
The practical difference is:
“No seek” therefore does not mean “sequential and random are identical.”
The following values are order-of-magnitude intuition for healthy local devices, not guarantees. Model, capacity, firmware, request size, queue depth, read/write mix, device fullness, and test duration can all change the result.
| Device class | Small random-read latency at low QD | Approximate peak 4 KiB random-read IOPS | Large sequential-read throughput |
|---|---|---|---|
| 7200-RPM HDD | Roughly 8–15 ms | Roughly 75–150 | Roughly 150–300 MB/s |
| SATA SSD | Roughly 50–200 µs | Roughly 80K–100K | Roughly 500–560 MB/s |
| PCIe 4 NVMe SSD | Roughly 20–100 µs | Hundreds of thousands to over 1M | Roughly 3–7.5 GB/s |
The columns do not describe one common benchmark. Low-queue latency reflects one or a few outstanding operations. The highest IOPS normally uses small requests and a much deeper queue. Peak sequential throughput uses large requests. Write performance can differ substantially from read performance. No real application receives all three best-case values simultaneously.
Concrete devices illustrate the scale:
These are useful anchors, not universal constants. An inexpensive SSD, a data-center SSD optimized for predictable latency, and a high-capacity consumer SSD can behave very differently even when they use the same interface.
SATA's nominal link rate is 6 gigabits per second. Bits and bytes are different units, and encoding plus protocol traffic consumes part of the link.
As a result, fast SATA SSDs commonly top out around 500–560 MB/s for sequential transfers. Installing faster NAND behind the same SATA interface cannot remove that interface ceiling.
NVMe over PCI Express provides much more link bandwidth and a queue model designed for concurrency. It can therefore expose more of a modern SSD controller's internal performance.
Consider this simplified result for 4 KiB random reads:
The numbers are internally consistent:
The result does not mean every 4 KiB read finishes in 210 µs. That is the average, while p99 shows that one percent took longer than approximately 780 µs.
It also does not describe queue-depth-one latency. The benchmark kept about 21 operations outstanding to achieve its IOPS. A dependent application that can issue only one request at a time may observe far lower IOPS even on the same device.
Before trusting any benchmark, identify:
Without this context, comparing two IOPS or MB/s values is usually meaningless.
Different backend operations stress different parts of the performance model.
A database index lookup often performs small random reads. Low-queue latency matters because one page can determine which page is needed next. Peak sequential bandwidth says little about this dependency chain.
A table scan, backup, media stream, or log replay tends to issue large sequential reads. Throughput matters more, and read-ahead or multiple requests can keep the device busy.
A write-ahead log produces mostly sequential writes, often in small batches. Batching adjacent records reduces operation count and increases bytes per request, but waiting longer to form a batch can add application latency.
A compaction or data-reorganization job may read and write large regions concurrently. It can consume most available bandwidth and create a queue that delays small foreground requests. The background job's throughput can look excellent while user-facing p99 latency becomes unacceptable.
This leads to a practical rule:
Optimize the metric that constrains the application, not the device's largest advertised number.
For a latency-sensitive service, the best operating point is often below maximum IOPS. For an offline scan, deeper queues and larger requests may be appropriate. The device is the same; the goal and workload are different.
Storage performance cannot be represented by one speed. Latency measures time per operation, throughput measures bytes per second, IOPS measures operations per second, and queue depth measures work in flight. Request size connects IOPS to throughput, while queue depth connects concurrency to utilization and latency.
HDDs strongly favor sequential access because random operations pay seek and rotational costs. SSDs remove mechanical seeks, but small requests, internal flash behavior, and available parallelism still affect performance. NVMe allows deep, distributed queues that can expose this parallelism, though the highest IOPS usually comes with more outstanding work and higher latency than a low-queue test.
Always interpret a result with its workload: request size, access pattern, read/write mix, queue depth, duration, and latency percentiles. The useful question is not “How fast is this device?” but “How does this device behave under the requests this application actually generates?”
5 quizzes