AlgoMaster Logo

Storage Performance

23 min readUpdated August 7, 2026
Listen to this chapter
Unlock Audio

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:

  • Latency describes how long one operation takes.
  • Throughput describes how many bytes move per second.
  • IOPS describes how many operations complete per second.
  • Queue depth describes how many operations are outstanding.
  • Access pattern describes the sizes, locations, and types of those operations.

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.

Starting with the Workload

Before comparing devices, describe what the application asks storage to do. At minimum, a useful workload description includes:

  • The request size, such as 4 KiB or 1 MiB
  • Whether locations are sequential or random
  • The proportion of reads and writes
  • How many operations can be outstanding at once
  • Whether later requests depend on earlier results
  • Whether the workload is a short burst or sustained

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: Time for One Operation

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:

  • Milliseconds: 1 ms = 0.001 s
  • Microseconds: 1 µs = 0.000001 s

An 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.

Average latency hides the tail

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:

  • p50 is the median: half the observations completed at or below it.
  • p95 is the point at or below which 95% completed.
  • p99 is the point at or below which 99% completed.

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 changes under load

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: Bytes per Second

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.

Large requests favor throughput

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.

Throughput belongs to the complete path

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: Operations per Second

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.

IOPS and latency at queue depth one

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: Work in Flight

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.

Queue depth can improve utilization

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.

A deeper queue is not automatically better

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...

Sequential and Random Access

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.

HDD access patterns

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.

SSD access patterns

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:

  • HDD random performance is dominated by mechanical positioning.
  • SSD random performance is dominated by flash latency, controller work, available parallelism, and internal media management.

“No seek” therefore does not mean “sequential and random are identical.”

Device-Class Numbers Worth Knowing

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 classSmall random-read latency at low QDApproximate peak 4 KiB random-read IOPSLarge sequential-read throughput
7200-RPM HDDRoughly 8–15 msRoughly 75–150Roughly 150–300 MB/s
SATA SSDRoughly 50–200 µsRoughly 80K–100KRoughly 500–560 MB/s
PCIe 4 NVMe SSDRoughly 20–100 µsHundreds of thousands to over 1MRoughly 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:

  • A 7200-RPM enterprise HDD can specify about 8 ms average seek, about 4.2 ms average rotational latency, and around 200 MB/s sustained transfer.
  • A high-end SATA SSD can advertise roughly 560 MB/s sequential reads, around 13K 4 KiB random-read IOPS at QD1, and around 98K at QD32.
  • A high-end PCIe 4 NVMe SSD can advertise roughly 7.45 GB/s sequential reads and more than one million random IOPS under a sufficiently parallel benchmark.

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.

Why SATA SSDs cluster near 550 MB/s

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.

Reading Benchmark Results Correctly

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:

  • Request size
  • Sequential or random placement
  • Read/write mix
  • Queue depth and number of workers
  • Average and percentile latency
  • Dataset size and whether caching affected the result
  • Test duration and whether the device reached sustained behavior

Without this context, comparing two IOPS or MB/s values is usually meaningless.

Applying the Metrics to Backend Workloads

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.

Summary

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?”

Quiz

Storage Performance Quiz

5 quizzes