SSD Bottleneck Analysis

A working page for modeling SSD performance as a series of capacity-constrained stages. Fill in device-specific numbers as they're gathered.

Method

Model the SSD data path as a chain of stages, each with its own maximum throughput (a "capacity," in the same sense as an arc in a transportation/network-flow problem). Data has to pass through every stage on the way from host to media, so the achievable end-to-end performance is bounded by the slowest stage:

SSD_Throughput(workload) = min( Stage_1, Stage_2, ..., Stage_N )

This is the same logic as a max-flow / min-cut problem: within a stage, parallel resources (lanes, channels, dies, planes) add capacity, but across stages the pipeline can only move data as fast as its narrowest link. The "bottleneck stage" is the equivalent of the minimum-capacity cut.

Two practical wrinkles versus a textbook transportation problem:

  • Units must match before taking the min. Some stages are naturally bandwidth-bound (GB/s) and others are naturally operation-bound (IOPS). Convert using the workload's block size: BW = IOPS x block_size and IOPS = BW / block_size. Which stages dominate depends on whether the workload is large-block/sequential (bandwidth-bound stages tend to win) or small-block/random (IOPS-bound stages tend to win).
  • The bottleneck can be time-varying. SLC write cache means the true minimum is different in "burst" mode (writing into cache) versus "sustained" mode (cache exhausted, writing/folding into native TLC/QLC). Compute both regimes separately.

Candidate Bottleneck Stages

BW_host = N_lanes x LineRate_GT/s x EncodingEfficiency / 8      [GB/s]
  • N_lanes: e.g., x4
  • LineRate_GT/s: Gen3 = 8, Gen4 = 16, Gen5 = 32
  • EncodingEfficiency: 128b/130b ≈ 0.9846 (Gen3+); 8b/10b = 0.8 (Gen1/2)
  • Subtract ~5-10% for NVMe/TLP framing overhead to get a realistic usable number.
  • Example: Gen4 x4 → 4 x 16 x 0.9846 / 8 ≈ 7.88 GB/s raw.

2. Controller Compute / FTL / Command Processing

IOPS_controller = N_cores x Clock_freq / CyclesPerCommand

or, if only a vendor spec is available, treat the published max IOPS number as this stage's capacity directly. Covers L2P (logical-to-physical) address translation, queue/doorbell handling, and command parsing. This is usually the limiter for small-block random IOPS on cheap/DRAM-less controllers.

3. DRAM Bandwidth (mapping table / cache)

BW_DRAM = DataRate_MTs x BusWidth_bits / 8 x N_channels          [GB/s]
IOPS_DRAM = BW_DRAM / Bytes_DRAM_access_per_IO
  • Each random IO needs L2P table read/update (a few bytes per 4KB mapped, more if page-level mapping) plus any data caching.
  • DRAM-less (HMB-based) designs borrow host memory instead — same math, but BW_DRAM is then constrained by the PCIe link too.

4. ECC / LDPC Encode-Decode Engine

BW_ECC = CodewordSize x DecodeRate_codewords_per_sec x N_engines   [GB/s]
  • Must exceed the raw NAND throughput at the required BER (bit error rate).
  • Degrades over drive life: more read-retry / decode iterations near end-of-life reduces effective DecodeRate.

5. NAND Flash Interface (channel bus / Toggle / ONFI)

BW_channel_total = InterfaceClock_MTs x BusWidth_bytes x N_channels   [GB/s]
  • Example: Toggle NAND at 800 MT/s, 1-byte bus, 8 channels → 0.8 x 1 x 8 = 6.4 GB/s aggregate raw.
  • This is a hard ceiling independent of how fast the NAND array itself can program/read.

6. NAND Media / Array (program, read, erase)

BW_media = (PageSize x Planes x Dies_per_channel x N_channels) / t_op   [GB/s]
IOPS_media = (Planes x Dies_per_channel x N_channels) / t_op
  • t_op = t_PROG (write), t_R (read), or amortized t_BERS / pages_per_block (erase).
  • This is the fundamental physical-array limit, parallelized across planes/dies/channels.
  • TLC/QLC t_PROG is much slower than SLC t_PROG — see SLC cache section below.

7. SLC (pseudo-SLC) Write Cache — Burst vs. Sustained

Burst_BW        = BW_media computed in SLC mode (1 bit/cell; fastest t_PROG)
Sustained_BW     = BW_media computed in native mode (TLC/QLC) minus folding overhead
Time_to_exhaust  = SLC_Cache_Size / (Host_Write_Rate)                 [if no concurrent folding]
  • Once the SLC cache fills, the drive must fold (relocate) data from SLC to native TLC/QLC in the background, competing with host writes for NAND interface + media bandwidth.
  • A simple sustained-state approximation:
    Sustained_BW ≈ BW_media(native) / (1 + FoldingOverheadFraction)

8. Sustained Random Write / Garbage-Collection-Bound Throughput

BW_host_sustainable = BW_media / WAF
  • WAF (Write Amplification Factor) = total NAND bytes written / host bytes written. GC relocates valid data, consuming NAND media + channel bandwidth that would otherwise serve host writes.
  • This is the stage most sensitive to workload (random vs. sequential, fill level, over-provisioning) — WAF can range from ~1.05x (sequential/FDP-managed) to 3-5x (worst-case random on a nearly-full drive).

9. Queue Depth / Concurrency Ceiling (Little's Law)

IOPS_QD = QD / Latency_per_op
  • Even if every downstream stage has spare capacity, the host can't exceed this if it doesn't issue enough outstanding commands to keep all channels/dies busy.
  • Useful as a sanity check: Required_QD_to_saturate = IOPS_media_stage x Latency_per_op.

10. Power / Thermal Throttling

Sustained_BW_thermal = Peak_BW x Duty_Cycle_allowed(T, PowerBudget)
  • No clean closed form — usually an empirical throttling curve from vendor data (clock/parallelism reduced above a temperature or power threshold).
  • Matters most for sustained sequential workloads and small form factors (M.2 without heatsink).

Aggregation

Convert every stage to the same unit for the workload of interest (GB/s for large-block/sequential, IOPS for small-block/random — or convert one to the other via block size), then:

SSD_Throughput(workload) = min(
    BW_host,
    IOPS_controller (x block_size if comparing to BW),
    BW_DRAM / IOPS_DRAM,
    BW_ECC,
    BW_channel_total,
    BW_media (or BW_media / WAF for sustained random writes),
    Burst_BW or Sustained_BW (SLC cache, depending on time regime),
    IOPS_QD,
    Sustained_BW_thermal
)

Compute this separately for each regime of interest — e.g., burst sequential write, sustained sequential write, random read QD1, random read QD32, sustained random write — since the binding stage typically differs across them.


Worked Example (template — fill in with real spec values)

Stage Formula Inputs needed Computed (GB/s) Computed (IOPS @ 4K) Notes
Host interface (PCIe) §1 lanes, gen
Controller compute §2 vendor spec or cores/clock
DRAM bandwidth §3 DRAM type, width, channels
ECC engine §4 codeword size, decode rate
NAND interface §5 interface speed, channels
NAND media (native) §6 t_PROG/t_R, planes, dies
SLC cache (burst) §7 SLC t_PROG, cache size
Sustained write (WAF-bound) §8 WAF estimate
Queue depth ceiling §9 QD, latency
Thermal §10 throttle curve
Bottleneck (min)

TODO — Spec Values to Gather Per Drive

  • [ ] PCIe generation and lane count
  • [ ] Controller model and rated max IOPS (vendor spec)
  • [ ] DRAM type/size/width (or DRAM-less / HMB)
  • [ ] NAND type (SLC/TLC/QLC), channel count, interface speed (Toggle/ONFI, MT/s)
  • [ ] t_PROG, t_R, t_BERS (from NAND datasheet or measured)
  • [ ] Planes per die, dies per channel
  • [ ] SLC (pseudo-SLC) cache size and mode (static/dynamic)
  • [ ] Expected/measured WAF for target workload
  • [ ] Thermal throttle temperature and duty-cycle behavior

Open Questions / Next Steps

  • Decide which workload profiles to model first (seq read/write, rand read/write at a few QDs).
  • Pick a reference drive (or several) to populate the worked-example table with real numbers.
  • Cross-check computed bottleneck against published/measured benchmark numbers to validate the model.