Majid Al-RaimiM:N sparsity on NVIDIA tensor cores

COE 592Lecture 4.2Part 10

M:N sparsity on NVIDIA tensor cores

Fine-grained structured sparsity keeps exactly N nonzeros in every block of M weights, compresses the matrix to half plus two-bit indices, and lets Ampere sparse tensor cores double GEMM throughput with no accuracy loss.

Concepts
4
Slides
78-83
Reading
24 min
Understood
0/4 concepts

Why this part matters

EIE proved that a sparse network can run faster and cooler than a dense one, but only on a chip nobody could buy. This part is the version you can run today. The same GPUs that train the models in your research project will accept a weight matrix in a fixed 2:4 pattern and double their matrix math, and NVIDIA ships the pruning recipe, the storage format and the library that does it.

Three things are worth taking from it. The exam will ask you to compute the storage of a 2:4 matrix, to explain how the tensor core uses two-bit indices to skip half its multiplies, and to justify why the accuracy tables show no loss. Beyond the exam, 2:4 is the reference point for every future claim you will read about a sparse accelerator: it is what a fixed, mild, hardware-friendly pattern buys, and where it stops.

By the end you can

  1. Define an a:b fine-grained structured pattern, state that 2:4 is 50 percent sparsity, and explain why a fixed count per block is what makes it hardware friendly.
  2. Compute the storage of a 2:4 compressed matrix (values plus 2-bit metadata) and the resulting saving for FP16 and INT8 weights.
  3. Trace how a sparse tensor core uses the 2-bit indices to select K/2 activations from a dense operand and halve the multiplies.
  4. Reproduce NVIDIA's train, prune, retrain recipe and explain why it recovers accuracy without a hyper-parameter search.
  5. Explain why GEMM speedup grows with K toward a 2x ceiling and why end-to-end inference gains are smaller.

2:4 sparsity: a pattern the hardware can count on

Look at the first row of the structured-sparse matrix on the slide. Read it four cells at a time. The first four hold a value, a zero, a zero, a value. The next four hold a zero, a value, a value, a zero. Every group of four consecutive weights along the row has exactly two survivors, and the survivors can sit anywhere inside the group. Mishra et al. write the metadata for that row as [[0, 3], [1, 2]]: the positions of the survivors, group by group.

That is the whole definition of 2:4 sparsity: for each group of four values along a row, at least two must be zero. The general form keeps the same shape. Write a pattern as a:b, where the first number is how many weights survive and the second is the block size. Sparsity is then fixed by the pattern, not by the data.

sparsity=1ab,2:4    124=50%\text{sparsity} = 1 - \frac{a}{b}, \qquad 2{:}4 \;\Rightarrow\; 1 - \tfrac{2}{4} = 50\%
Sparsity of an a:b pattern

2:4 is the instance that ships. Ampere sparse tensor cores accept it for FP16, BF16 and INT8 weights, and for TF32 the block halves to 1:2, still 50 percent (Mishra et al., section 3.2). NVIDIA describes the pattern as "two non-zero values in every four-entry vector" and calls it fine-grained structured sparsity (NVIDIA Ampere in-depth blog). The A100 whitepaper is explicit that the pattern is enforced along rows.

Fine-grained in what it removes, structured in how many

Lecture 04-1 laid out pruning granularities on a line from irregular fine-grained pruning, which may zero any single weight, to channel pruning, which removes whole filters. 2:4 sits between them and borrows the best of each. It is fine-grained because the unit removed is a single weight, so the pruning criterion still gets to pick within every block, which is why accuracy survives. It is structured because the count per block is constant, so the hardware knows in advance that every four weights hide exactly two multiplies.

That constancy is the entire engineering payoff. Unstructured sparsity, the kind EIE handles, needs a data-dependent index for every nonzero and a pointer to the start of every column, and the paper notes it leads to poor utilization of cache lines because the survivors land anywhere. With 2:4, the paper points out, the sparsity is constant across the matrix, so no indirection is required: a nonzero's position in memory can be computed directly from the compression rate. Two bits per survivor say which of the four slots it came from, and nothing else needs to be stored.

PatternWhat can be removedMetadataHardware that benefitsFlexibility for accuracy
Unstructured (EIE)Any weight, anywhereIndex per nonzero, pointers, paddingCustom accelerator or slow CPU kernelsHighest, chosen per weight
2:4 fine-grained structuredAny two of every four in a row2 bits per stored value, no pointersEvery Ampere and later GPUHigh, chosen per block
Channel pruningWhole output channelsNone, matrix just shrinksAny dense hardwareLowest, chosen per channel
Three granularities, what they cost to describe and where they run

One more fact will matter in the next two concepts: the groups of four run along the row of the weight matrix W. In the GEMM that a layer becomes, that row is the reduction dimension, the axis along which products are summed. The pattern has to live there, because that is the only axis on which skipping a weight also skips a multiply.

Where this sits among the three case studies

The divider slide that opens this section lists three sparse-hardware case studies. EIE (Efficient Inference Engine) came first: a custom accelerator exploiting Weight sparsity and Activation sparsity at once. This part is the middle one, and it takes the opposite bet. It exploits weight sparsity only, at a fixed and modest 50 percent, in exchange for running on a GPU that already sits in every data center. TorchSparse and PointAcc, which follow, go back to activation sparsity in point clouds. Keeping the three apart by which sparsity they exploit and what hardware they need is the fastest way to remember them.

Recall

Why is 2:4 called fine-grained structured sparsity?

Fine-grained because individual weights are removed, so the criterion still chooses within each block. Structured because every block of four keeps exactly the same count, so positions need only 2-bit tags and no indirection.

Take one group of four FP16 weights. Dense, it costs 4 x 16 = 64 bits. Under 2:4, only two values are stored, 2 x 16 = 32 bits, plus a 2-bit index for each, 2 x 2 = 4 bits. The group now costs 36 bits, a saving of about 44 percent (Mishra et al., section 3.1). Not 50 percent: the indices are small but they are not free.

Scale that to a whole matrix and you get the format on the slide. A structured-sparse W of size R x C is stored as an R x C/2 block of nonzero values and an R x C/2 block of two-bit indices. The slide's instruction is to push all the nonzero elements to the left in memory. The survivors of each row are packed contiguously at half the original width, and the index block, drawn beside them, records where each came from.

Each row loses its zeros, its survivors slide left into a half-width value block, and a strip of 2-bit tags records which of the four slots each value came from
bits2:4=RC2bvalues+RC22indices,bitsdense=RCb\text{bits}_{2:4} = \underbrace{R \cdot \tfrac{C}{2} \cdot b}_{\text{values}} + \underbrace{R \cdot \tfrac{C}{2} \cdot 2}_{\text{indices}}, \qquad \text{bits}_{\text{dense}} = R \cdot C \cdot b
Storage of a 2:4 compressed matrix with b-bit values

Worked example

An FP16 layer of 1024 x 1024 weights

  1. Dense storage

    1024 x 1024 x 16 = 16,777,216 bits, which is 2 MiB.
  2. Stored values

    Half the columns survive: 1024 x 512 = 524,288 values at 16 bits each, 8,388,608 bits = 1 MiB.
  3. Metadata

    One 2-bit index per stored value: 524,288 x 2 = 1,048,576 bits = 128 KiB.
  4. Total and saving

    8,388,608 + 1,048,576 = 9,437,184 bits = 1.125 MiB. The ratio to dense is 9,437,184 / 16,777,216 = 0.5625.
  5. Result

    43.75% saved. The value array halves; the indices add back an eighth of what remains.
CaseDenseValuesMetadataTotalSaving
FP16, R = C = 102416,777,216 (2 MiB)8,388,608 (1 MiB)1,048,576 (128 KiB)9,437,184 (1.125 MiB)43.75%
INT8, R = C = 4096134,217,728 (16 MiB)67,108,864 (8 MiB)16,777,216 (2 MiB)83,886,080 (10 MiB)37.5%
The same arithmetic for the two shipping weight widths, in bits

The index cost is a fixed 2 bits per stored value, so its weight relative to the values depends on the value width: 2 / 16 = 12.5% overhead for FP16 and 2 / 8 = 25% for INT8, which is why the INT8 saving is the smaller 37.5 percent (Mishra et al., section 3.1). Compare that with the format EIE relied on. CSC stores a 4-bit relative index and a 4-bit weight-sharing code per nonzero, a 16-bit pointer per column, and a padding entry whenever a run of zeros exceeds fifteen (Han et al., EIE, section III). Mishra et al. note that a plain CSR with 8-bit weights and 16-bit column indices can spend up to 200 percent of the value bits on metadata. 2:4 spends 12.5 or 25 percent, with no pointers and no padding, because the pattern itself carries most of the position information.

One group of four weights under 2:4

FP16 dense
4 x 16 = 64 bits
FP16 compressed
2 x 16 + 2 x 2 = 36 bits, about 44% saved
INT8 dense
4 x 8 = 32 bits
INT8 compressed
2 x 8 + 2 x 2 = 20 bits, about 38% saved

Why insist on pushing everything to the left rather than leaving zeros in place and skipping them? Because memory is read in wide lines. With the survivors contiguous at half width, every byte a memory read brings in is a value the tensor core will use, which the paper describes as letting hardware fully utilize large memory reads. The A100 whitepaper puts the same point as a reduction of memory storage and bandwidth by almost 2x. Zeros left in place would fill half of every line with nothing.

Simulator2:4 storage and speedup calculator
R = 1024
C = 1024
f = 0.70
1.80x
stored values = R × C × 2/4 = 524.3 kvalue bits = 524.3 k × 16 b, metadata bits = 524.3 k × 2 bsaving = 1 − (value + metadata) / dense = 43.75%
Dense weights2.00MiBR × C × 16 b
Stored values1.00MiB2 of every 4
Metadata128KiB2 b per stored value
Compressed total1.13MiBvalues plus indices
Storage saving43.75%below 50 because of the indices
Metadata overhead12.50%index bits over value bits
MACs per GEMM536.87 MMACdense 1.07 G with M = N = K = C
End-to-end speedup1.45xAmdahl: 1 / ((1 − f) + f / s)

Check against Mishra et al.: 43.75 percent, the paper's about 44 percent for FP16.

The value array always halves under 2:4, but every stored value drags a 2-bit index with it, so the saving lands at 43.75 percent for 16-bit weights and 37.5 percent for 8-bit weights, never at 50. The MAC count halves exactly, which is the 2x the sparse tensor core can deliver on the matrix math alone. The last readout applies Amdahl to the whole network: with 70 percent of inference time inside GEMMs and a measured 1.8x on those GEMMs, the model runs about 1.45x faster. Drag the GEMM share down to see why memory-bound layers and small batches hide most of the gain.

Quick check

A 2:4 sparse FP16 weight matrix has R = 512 rows and C = 1024 columns. How many metadata bits does the compressed format hold?

Recall

For an R x C weight matrix in 2:4 format, how many values and how many metadata bits are stored?

R x C/2 values and R x C/2 x 2 = R x C metadata bits, so the metadata is 2 bits per stored value, or one bit per original weight position.

Follow one output element through a small GEMM with K = 8. Dense, the tensor core takes a row of A with eight values, a column of B with eight values, multiplies them pairwise and accumulates eight products. Now store that row of A in 2:4 form: four values and four 2-bit indices. The core reads the indices, pulls exactly the four elements of the B column that sit at those positions, multiplies four pairs, and accumulates. The slide's caption says it in one line: the indices are used to mask out the inputs, and only two multiplications will be done out of four.

Two compressed A values carry tags 00 and 11. The tags open two of the four gates on the dense B column, the two chosen pairs meet at the multiplier, and one result lands in the accumulator. The two greyed inputs are never fetched.

The rule behind the example is worth stating precisely, because exam questions turn on it. A Sparse tensor core performs sparse matrix times dense matrix equals dense matrix. Only the first operand, A of size M x K, is compressed, to M x K/2 values plus indices. The second operand B (K x N) and the output C (M x N) stay dense (Mishra et al., section 3.2). NVIDIA's TensorRT blog describes the mechanism as using the metadata to pull only the necessary values from the other, uncompressed operand. The metadata belongs to the weights, but it is applied to the activations.

A, compressed
K/2 values plus 2-bit indices

The weight row, already packed left.

indices
Selector
choose matching K/2 of K

A multiplexer over the dense B column.

K/2 pairs
Multiply and accumulate
half the products

Writes a dense element of C.

One sparse tensor core step: the 2-bit indices route the dense operand through a selector so that only K/2 products are formed

Halving the pairs halves the work, and the hardware is built so that the saving shows up as time. The A100 whitepaper states that a standard MMA on a 16 x 8 x 16 tile takes some number of cycles N, and the sparse MMA on the same tile takes N/2, a 2x speedup. The paper's Table 1 reports the resulting peak rates.

A100 peak dense versus sparse tensor throughput, in TOPS (Mishra et al., Table 1)

TF32 (1:2 pattern)
156 dense, 312 sparse
FP16 / BF16
312 dense, 624 sparse
INT8
624 dense, 1248 sparse

Why must the pattern run along K? Because K is the axis the selector walks. Each output element is a dot product over K, and a zero weight at position k means the product with B[k, n] can be dropped for every n. Zeros arranged along M or N would zero entire rows or columns of the output, which is a smaller network, not a faster GEMM. This is also why the library has shape rules: the reduction dimension must be a multiple of 16 for 16-bit formats and 32 for INT8, and layers that fail the rule, such as the first convolution of an image network with K = 3 x 7 x 7 = 147, are simply left dense (Mishra et al., sections 3.2 and 5.1). cuSPARSELt exposes the compression and the sparse GEMM to programmers, and TensorRT 8 applies them automatically to a network whose weights already follow the pattern.

What changed since EIE

Set the two designs beside each other and the trade becomes visible. EIE (Efficient Inference Engine) reaches for every zero it can find: about 90 percent in the weights and about 70 percent in the activations, both exploited at once. To do that it needs Leading non-zero detection to find the next live activation, a FIFO in front of each Processing element (PE) to keep Load balance across uneven columns, and a custom chip to hold all of it. The sparse tensor core asks for far less sparsity, exactly 50 percent in the weights alone, which is static and known before the run. That is what lets the whole mechanism collapse into a multiplexer driven by 2-bit tags, small enough to add to a tensor core that every GPU already has.

AspectEIE (ISCA 2016)2:4 sparse tensor core (2020)
Sparsity exploitedWeights (about 90 percent) and activations (about 70 percent)Weights only, exactly 50 percent
Weight formatCSC: 4-bit relative index plus 4-bit code per nonzero, column pointers, paddingContiguous values plus one 2-bit index per stored value
Finding workLeading nonzero detection, activation FIFO for load balanceMultiplexer driven by the 2-bit indices, no search
HardwareCustom 45 nm ASIC, never soldEvery Ampere or later GPU, cuSPARSELt and TensorRT
Ceiling on mathProportional to combined sparsity2x, fixed by the pattern
EIE versus 2:4 on a sparse tensor core

Quick check

Which statement about 2:4 sparse GEMMs on Ampere sparse tensor cores is correct?

Quick check

Compared with EIE, what makes 2:4 sparsity easy to deploy on commodity hardware?

Recall

Which GEMM operand is stored sparse, which is dense, and along which dimension must the 2:4 pattern run?

The weight operand A (M x K) is sparse and compressed to M x K/2. B (K x N) and C (M x N) are dense. The pattern runs along K, the reduction dimension.

ResNet-50 on ImageNet scores 76.1 top-1 dense in FP16. After 2:4 pruning and retraining it scores 76.2 in FP16 and 76.2 in INT8 (Mishra et al., Table 2). Half the weights are gone from almost every layer, the GEMMs can run on the sparse path, and the accuracy did not move. The slide's takeaway is the plain version of this: pruning CNNs with 2:4 sparsity brings a large speedup for GEMM workloads and does not incur a performance drop for the models. The rest of this concept explains how that is achieved, how large the speedup really is, and where both claims stop.

The recipe: one prune, one retrain, no search

The deck does not show the procedure itself, so take it from the paper (Mishra et al., section 4) and from the A100 whitepaper, which calls it a simple and universal recipe. It has three steps.

  1. Train the model without sparsity, exactly as you normally would.
  2. Prune it to 2:4 by magnitude: in every group of four weights along the row, zero the two with the smallest absolute value.
  3. Retrain from those weights using the same optimizer, learning-rate schedule and number of epochs as step 1, with the zeros held fixed so the pattern survives. Optimizer state such as momentum is reset.

Two things distinguish this from the Fine-tuning and Iterative pruning you met earlier in this lecture. It is one-shot: a single prune straight to the target, then a full second training run rather than a short low-learning-rate touch-up. And it needs no hyper-parameter search, because step 3 reuses step 1 wholesale. The paper is candid about the cost, a second full training, and argues it is worth paying once for a workflow with nothing to tune, since deployment amortizes it. NVIDIA packages the procedure as the ASP library for PyTorch.

How much faster: a staircase toward 2x

The chart on the slide compares INT8 GEMMs from cuSPARSELt, with one operand in 2:4 form, against dense cuBLAS GEMMs on an A100, at M = N = 10240 and K sweeping from 1280 to 20480. The bars start near 1.2x and climb toward 2x without reaching it. Mishra et al. read it the same way: larger GEMMs achieve nearly a 2x speedup with sparse tensor cores.

Speedup bars from the A100 chart rise with GEMM-K and flatten just below the dashed 2x line, which is the most the halved math can ever give
GEMM-KSpeedup
1280about 1.2x
2560about 1.5x
3840about 1.7x
7680about 1.8x
12800about 1.9x
20480about 1.95x
INT8 sparse versus dense GEMM speedup on A100, read from the slide's chart (approximate)

Why does the speedup depend on K at all, when the pattern halves the multiplies at every size? Because the sparse tensor core halves only the arithmetic. A GEMM also has to move its operands in and its result out, and it pays fixed launch and scheduling costs. A small GEMM has low arithmetic intensity: few operations per byte moved, so its time is dominated by memory traffic and overheads that 2:4 does not touch. As K grows, the operations per output element grow while the output traffic does not, the GEMM becomes math bound, and halving the math approaches halving the time. The paper says exactly this: larger GEMMs tend to have higher arithmetic intensity, so they get closer to the 2x speedup.

Worked example

From GEMM speedup to model speedup with Amdahl

  1. Name the fraction that improves

    Suppose GEMMs take 70% of inference time (f = 0.7) and the sparse path makes them 1.8x faster (k = 1.8), a typical large-K reading from the chart.
  2. Apply Amdahl's law

    S = 1 / ((1 - 0.7) + 0.7 / 1.8) = 1 / (0.3 + 0.389) = 1 / 0.689.
  3. Repeat for a small-K layer

    With k = 1.2 the GEMM term is 0.7 / 1.2 = 0.583, so S = 1 / 0.883 = 1.13x.
  4. Result

    About 1.45x end to end at 1.8x on the GEMMs, and only 1.13x when the GEMMs are small. The pattern is doing its job on the math either way; the rest of the network decides what the user sees.

Accuracy: the table that justifies the claim

The right half of the slide is the paper's Table 2: twenty ImageNet classifiers, each in dense FP16, sparse FP16 and sparse INT8. Read down any row and the three numbers agree to within a few tenths of a point; the widest spread is SUNet-128 at 75.6, 76.0 and 75.4. The paper states that the differences are within run-to-run variation, which is the honest way to say zero loss.

NetworkDense FP16Sparse FP16Sparse INT8
ResNet-3473.773.973.7
ResNet-5076.176.276.2
ResNet-10177.778.077.9
ResNeXt-101-32x16 (WSL)84.284.084.2
DenseNet-12175.575.375.3
Inception v377.177.177.1
VGG-1674.074.174.1
DRN-10579.479.579.4
ImageNet top-1 accuracy, dense versus 2:4 sparse (Mishra et al., Table 2, selected rows)

Two details in that table repay attention. Sparse INT8 matches sparse FP16, so the pruning and INT8 quantization, which this course turns to after pruning, compose without compounding their losses, at least on these networks. And the largest model, ResNeXt-101-32x16 trained with weak supervision, loses 0.2 in FP16 and nothing in INT8, so the pattern is not exploiting a weakness of small or old architectures. VGG, DenseNet, Inception, Xception and the dilated residual networks all behave the same way.

NetworkDense2:4, plain recipe2:4 with permutation
MobileNet v271.5569.5671.56
EfficientNet B077.2575.9877.29
Parameter-efficient networks under the plain recipe and with channel permutation (Mishra et al., Table 3)

Quick check

On the A100 chart, why does the sparse versus dense INT8 GEMM speedup climb toward 2x only as GEMM-K grows?

Recall

State the three steps of NVIDIA's 2:4 recipe and the one hyper-parameter search it needs.

Train dense; prune the two smallest of every four weights along each row; retrain from those weights with the same optimizer, schedule and epochs while keeping the zeros fixed. It needs no hyper-parameter search at all.

Recall

Why is the INT8 speedup only about 1.2x at K = 1280 but about 1.95x at K = 20480?

Sparse tensor cores halve only the math. Small GEMMs have low arithmetic intensity and are bound by memory traffic and fixed costs, so halving the math barely moves the wall clock. Large GEMMs are math bound and approach the 2x ceiling.

Recap

If you remember nothing else

  • 2:4 keeps at most two nonzeros in every four consecutive weights along a row: 50 percent sparsity, fine-grained in what it removes, structured in how many it removes.
  • Compressed W = R x C/2 values plus R x C/2 two-bit indices. Per group of four, 64 bits become 36 (FP16, about 44 percent saving) and 32 become 20 (INT8, about 38 percent).
  • The pattern runs along GEMM-K. The sparse tensor core uses the indices to pick K/2 matching elements of the dense B operand, so only half the multiplies run.
  • Peak A100 throughput doubles: FP16 312 to 624 TOPS, INT8 624 to 1248 TOPS.
  • Recipe: train dense, prune the two smallest of every four, retrain with the identical schedule keeping the zeros fixed. No hyper-parameter search.
  • Sparse FP16 and INT8 match dense FP16 within run-to-run noise on 20 ImageNet CNNs (ResNet-50 76.1 versus 76.2). Small nets like MobileNet v2 need channel permutation to recover.
  • INT8 GEMM speedup rises from about 1.2x at K = 1280 to about 1.95x at K = 20480. The 2x caps the math only; end-to-end gains are smaller (about 1.2x for ResNeXt-101 in NVIDIA's blog).
  • Versus EIE: a fixed pattern and 2-bit tags on a commodity GPU instead of per-value indices, load balancing and a custom ASIC.

Sources