pub struct Work {Show 15 fields
pub matmul_retired: u64,
pub noc_txns: u64,
pub noc_bytes: u64,
pub payload_bytes: u64,
pub tensix_inst: u64,
pub compute_calls: u64,
pub engine_operand_tiles: u64,
pub sites_used: u64,
pub site_noc_bytes: u64,
pub engine_noc_bytes: u64,
pub site_noc_txns: u64,
pub site_matmul_retired: u64,
pub dram_bytes: u64,
pub dram_tiles: u64,
pub onchip_bytes: u64,
}Expand description
What a program costs the machine, in the units the machine can actually report.
Two views, because they are two different questions and a compiler that owns a Pareto frontier has to keep them apart:
- the DEVICE TOTALS are what the whole machine spends — the energy question, and the only view the work meter measures, so the only one that can be validated here;
- the BUSIEST SITE is what one site spends — the balance question, and what a latency model on silicon would be built from. It is PREDICTED STRUCTURE and is deliberately not called a time: ttsim has no clock that measures the program, so nothing here may be quoted as speed.
Minimizing the totals alone would collapse a small problem onto ONE site — that genuinely does move the fewest bytes — which is why the balance term exists and why the objective is stated rather than assumed.
Fields§
§matmul_retired: u64MVMULs that retire. Closed-form and exact; the conservation law.
noc_txns: u64NOC transactions initiated. One per tile on an interleaved buffer, one per multicast.
noc_bytes: u64Bytes moved over the NOC, DRAM traffic, multicast delivery and four-byte handshakes alike. This is what the work meter counts, so this is the one that gets validated against the machine.
payload_bytes: u64The DATA subset of noc_bytes: tiles, with the four-byte control writes left out.
★ THE OBJECTIVE RANKS ON THIS, NOT ON noc_bytes, and the reason is a pathology it caused. A
semaphore atomic moves four bytes and costs a transaction SLOT; putting those four bytes into a
bandwidth term let a plan win on bandwidth by eight bytes while losing on transactions by 2,160.
Bandwidth and request-rate are different resources and the model keeps them apart.
tensix_inst: u64Tensix instructions issued across the device.
compute_calls: u64Matrix-engine API invocations the choreography issues across the device.
★ THE ISSUE-COST TERM, AND IT IS A COUNT RATHER THAN A FIT. Two plans can move exactly the same
bytes in exactly the same transactions and still differ enormously in what the compute engine is
asked to do, because the SUB-BLOCK decides how much arithmetic one call covers: at
4096x4096x256 a 1x8 sub-block issues 16,384 matmul_block calls and a 1x1 sub-block issues
131,072 for identical movement — measured 1,781,904 Tensix instructions against 6,383,760, a
3.58x difference the movement axes cannot see at all.
Predicting the instruction count itself would need per-call constants fitted to something; the CALL COUNT needs nothing — it is read off the same choreography as everything else, and at equal movement and equal retired arithmetic, fewer calls is unambiguously less work.
engine_operand_tiles: u64Operand tiles the matrix engines load across the device, each engine by its own rule
(SpatialTarget::operand_loads). At equal calls the sub-block’s SHAPE decides it: the fabric’s array
is fed a square sub-block’s operands in fewer elements than a long one of the same area, and Tensix’s
unpacker, which holds one operand and re-loads the other for every held tile, the reverse.
sites_used: u64Sites the program actually placed work on.
site_noc_bytes: u64Data bytes crossing the BUSIEST site’s own network interface.
engine_noc_bytes: u64Data bytes crossing the BUSIEST MOVEMENT ENGINE of any site.
★ A TENSIX SITE HAS TWO MOVEMENT ENGINES AND THEY RUN CONCURRENTLY, so what paces a site is not what
it moves but what its busier engine moves. This is the term that tells apart two plans which fetch
exactly the same tiles and differ only in which engine carries which stream — issuing both
operand reads together leaves the store an engine to itself and serialises the reads; pairing the
second operand with the store overlaps a block’s write with the next block’s reads. Without it
the two are indistinguishable, and the compiler picked between them by coin. A site with one
engine (SiteSpec::movement_engines) carries every stream on it.
site_noc_txns: u64NOC transactions the busiest site initiates.
site_matmul_retired: u64MVMULs the busiest site retires.
dram_bytes: u64The DRAM subset of noc_bytes: operand reads and output writes of the tensors off the chip’s SRAM tier,
excluding L1-to-L1 multicast — what leaves the chip. A tensor the program holds in SRAM
(super::residency) moves on chip, and is counted in onchip_bytes instead.
★ THIS IS THE SHARED RESOURCE, and on a GEMM it is the one that binds. Wormhole has SIX DRAM channels against sixty-four sites, so a plan whose sites each fetch their own operands puts an order of magnitude more pressure on DRAM than one that fetches each tile once and multicasts it — even though both deliver the same bytes to the same sites. A model that looked only at per-site traffic would call those two plans equal, and they are not.
dram_tiles: u64The tiles behind dram_bytes, whatever each tensor’s width: what a lane that carries one element per
cycle is paced by, where a bus is paced by the bytes.
onchip_bytes: u64Tile bytes of the tensors the program holds in its SRAM tier — intermediates and carried state it keeps on chip, read and written across the crossbar without leaving it.
Implementations§
Trait Implementations§
impl Copy for Work
impl Eq for Work
impl StructuralPartialEq for Work
Auto Trait Implementations§
impl Freeze for Work
impl RefUnwindSafe for Work
impl Send for Work
impl Sync for Work
impl Unpin for Work
impl UnsafeUnpin for Work
impl UnwindSafe for Work
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.