Phase 2, week 11retrospective

Bias fusion

Bias added once after the reduction is bit-identical to the reference; seeding the accumulator with it is legal but changes every partial.


A broadcast is a map, not a copy

The bias is a vector over the output column, so its indexing map drops the row iterator:

#out   = affine_map<(m, n) -> (m, n)>
#col   = affine_map<(m, n) -> (n)>
#attrs = {indexing_maps = [#out, #col, #out], iterator_types = ["parallel", "parallel"]}
func.func @bias_relu(%acc: memref<32x32xf32>, %bias: memref<32xf32>,
                     %c: memref<32x32xf32>) {
  %zero = arith.constant 0.0 : f32
  linalg.generic #attrs ins(%acc, %bias : memref<32x32xf32>, memref<32xf32>)
                       outs(%c : memref<32x32xf32>) {
  ^bb0(%a: f32, %b: f32, %o: f32):
    %s = arith.addf %a, %b : f32
    %r = arith.maximumnumf %s, %zero : f32
    linalg.yield %r : f32
  }
  return
}

The map is what makes the broadcast explicit, and bufferization sees an operand of shape n, not a materialized m by n tensor. In the microkernel the same thing is one vle32 at bias + j with the loop’s vl, followed by vfadd_vv.

Two ways to add a bias

C = A*B + bias as an epilogue add, or C initialised to the broadcast bias so the accumulation starts from it. Both are correct; only the first is bit-identical to an unfused reference. Seeding puts the bias inside every partial sum, so each of the K MACs rounds a different value. If the harness checks bit equality, keep the add after the loop and say so in the test’s name.

What it removes

One read and one write of the output tile: 8 bytes per f32 element, or one full traversal of the output. The bias vector itself is read once per tile column block, so its traffic is smaller by the row-blocking factor and does not show up in the byte count.

Things that break

The bias load uses VLMAX while the store uses vl, so the last strip reads past the bias allocation; the result is usually still right, which is what makes it survive review. The other failure is the map: an operand indexed by m when the output tile was written transposed gives plausible numbers in the wrong places, and only a hand-checked element or a non-square test shape exposes it.

Sources

A version records the documentation the claim was checked against, not a house pin. Confirm flags with --help on the release you installed.

Check yourself

Which indexing map does a per-column bias use in a matmul epilogue?

Which bias placement is bit-identical to an unfused reference?

The epilogue adds bias and stores the tile. Which `vl` should the bias load use?

0 / 3 correct

← back to the map