LastAxis flops bf16 global=25769803776 per-unit=12884901888 ratio 2.0 1820708 predicted-ns
StripMajor flops bf16 global=25769803776 per-unit= 201326592 ratio 128.0 128676 predicted-ns
Two modules, identical arithmetic and traffic; the only difference is the two marked lines.
from __future__ import annotations
from tilefoundry import func, module
from tilefoundry.dsl import ConstTensor, DimVar, Mesh, Tensor, tf
from tilefoundry.dsl.tf import * # noqa: F401, F403
from tilefoundry.ir.types.shard import Topology
from tilefoundry.target import CudaTarget
H, GRID, PT, BK, NOUT, NPER = 2048, 128, 64, 128, 12288, 96
S = DimVar("seq_len", 0, 8193)
_H200, _CTA = CudaTarget("nvidia.h200_sxm"), Topology("cta", GRID)
@module(entry="proj", target=_H200, topologies=(_CTA,))
class LastAxis:
@func
def proj(x: Tensor[(1, S, H), "bf16"],
w: ConstTensor[(H, NOUT), "bf16"], # (K, N)
out: Tensor[(1, S, NOUT), "bf16"]) -> Tensor[(1, S, NOUT), "bf16"]:
with Mesh(("cta",), layout=(GRID,), names=("u",)) as m:
acc = out
for t in tile(S, PT):
base = t + 0
xs = tf.reshard(x[:, base:base + PT, 0:BK], (1, PT, BK), "smem")
ws = tf.reshard(w[0:BK, :], (BK, NOUT @ m.u), "smem") # <-- last axis
acc = tf.insert_slice(acc, tf.matmul(xs, ws), (0, base, 0))
return acc
@module(entry="proj", target=_H200, topologies=(_CTA,))
class StripMajor:
@func
def proj(x: Tensor[(1, S, H), "bf16"],
w: ConstTensor[(GRID, H, NPER), "bf16"], # strip-major
out: Tensor[(GRID, S, NPER), "bf16"]) -> Tensor[(GRID, S, NPER), "bf16"]:
with Mesh(("cta",), layout=(GRID,), names=("u",)) as m:
acc = out
for t in tile(S, PT):
base = t + 0
xs = tf.reshard(x[:, base:base + PT, 0:BK], (1, PT, BK), "smem")
ws = tf.reshard(w[:, 0:BK, :], (GRID @ m.u, BK, NPER), "smem") # <-- leading
acc = tf.insert_slice(acc, tf.matmul(xs, ws), (0, base, 0))
return acc
Why
MatMulper-unit flops come outglobal / 2— whatever the grid is — when the sharded axis of the output is its last one. The same gemm with the sharded axis leading reportsglobal / 128, which is right for a 128-CTA mesh.performanceis issued against that column, so two spellings of one gemm read 14x apart onpredicted-nswhile moving the same bytes and reporting the sameideal-ns.What
visitor_registry/op_cost.py::_matmulreads shapes off the local types:(units, per-unit). Sharded last, the new axis lands at-2: the local output is(1, 64, 1, 96), sobatch = prod((1, 64)) = 64andm = 64too.Mis counted twice and theunitsdivisor never appears. Sharded leading, the new axis lands inside the batch region at extent 1 and the arithmetic is right.caston the same value in the same program reportsglobal / 128, so this is the shape arithmetic in_matmul, not the local projection.Contract
compute-cost, and everypredicted-nsderived from it, is currently a function of which axis a weight's layout puts the grid on.--topology ctais documented as changingflops_per_unitfor a program that shards; a sharded program reportingglobal / 2is reporting that it barely sharded.flops(global) andideal-nsare unaffected.Risk
flops_per_unit/predicted-nsfor existing last-axis-sharded programs — in the direction of the grid divisor.m,k,nand the batch from operand/result tensor axes rather than local layout ranks.layout_axis_to_tensor_axisis already in that file and_conv2dbeside it uses it for the same reason.Reproducer
Two modules, identical arithmetic and traffic; the only difference is the two marked lines.
tilefoundry 0.0.2.dev43+g45032b967(45032b9), targetnvidia.h200_sxm.