pub struct Hardware {
pub properties: Arc<DeviceProperties>,
pub accumulator_as_a: bool,
}Expand description
The device, as the routines see it.
A value rather than a client, so a routine’s geometry is unit-tested against a device that is not in the room. Cloning one costs a count increment: the properties are the client’s own.
Fields§
§properties: Arc<DeviceProperties>What the runtime reports about the device. Every capability and limit a routine adapts to is read here, as cubecl states it.
accumulator_as_a: boolWhether an mma accumulator holds its cells where the A operand of a contraction as
deep as it is wide holds them, so a product contracted again reads it as that operand in
the same registers (the tile DSL’s accumulator_as_a).
True on CUDA, and on Metal, whose simdgroup matrix gives every lane two cells of one row
in each operand. False on RDNA, whose A runs along its rows against an accumulator down
its columns, and whose RDNA3 A is held twice across the wave.
Implementations§
Source§impl Hardware
impl Hardware
Sourcepub fn new(properties: Arc<DeviceProperties>, target: &TargetProperties) -> Self
pub fn new(properties: Arc<DeviceProperties>, target: &TargetProperties) -> Self
The device properties describe, compiling for target.
Sourcepub fn from_client(client: &Client) -> Self
pub fn from_client(client: &Client) -> Self
The device client runs on, compiling for its target.
Sourcepub fn plane_dim(&self) -> Result<u32, Refusal>
pub fn plane_dim(&self) -> Result<u32, Refusal>
The exact plane width, for a kernel whose correctness depends on one.
The only way a routine reaches a plane width, and fallible, because both shortcuts are
wrong. plane_size_max > 1 does not mean planes work: the wgpu runtime reports
8..=128 for an adapter with no subgroups at all, and a plane_sum there is undefined.
And plane_size_max is not the width: wgpu reports a range on AMD RDNA (32 to 64) and
Intel (8 to 32), and a cube sized to the max sums over a fraction of each row.
§Errors
Refusal::PlanesUnavailable without plane operations, Refusal::ImprecisePlaneDim
where the runtime reports a range.
Sourcepub fn plane_size(&self) -> Result<u32, Refusal>
pub fn plane_size(&self) -> Result<u32, Refusal>
Units that cooperate as one plane: the device’s width where it commits to one, and 1
where a plane is a single unit.
The width a derivation asks for. plane_dim is the width a kernel asks
for when it issues plane instructions; a geometry that only divides a microkernel between
cooperating units gets 1 on a runtime with none, the width the tile engine builds its
cube from there. A single-unit plane is read off what makes it one, a committed width of
one and no plane operations, as cubecl’s CPU runtime reports.
§Errors
plane_dim’s, for a fabricated width or a range.
Sourcepub fn dispatch_plane_size(&self) -> u32
pub fn dispatch_plane_size(&self) -> u32
The width the engine builds a cube from, whatever the device committed to: sound only
where no axis of the space rides Unit scope.
cubek builds cube_dim.x from plane_size_max and asserts only that the axes riding
Unit scope multiply to it. A cut that hands disjoint work to whole planes has no Unit
axis, so this is the width the launch is going to use rather than a guess about the
device. Units that share an item and sum across each other need
plane_size instead.
Sourcepub fn full_cube_planes(&self) -> usize
pub fn full_cube_planes(&self) -> usize
The most planes of the width the device dispatches that one cube holds: a cube as full as the device allows. On a CPU runtime, where a plane is one unit, a unit on every core.
Sourcepub fn cube_dim_1d(&self, units: u32) -> CubeDim
pub fn cube_dim_1d(&self, units: u32) -> CubeDim
A flat cube of units, clamped to what the device spawns: for the element-wise kernels
that want a reasonable block, where the count is a throughput preference.
Shared memory a cube may claim: the reported maximum, less what the driver reserves for cube-scoped matrices. Every budget goes through here: reading the maximum directly over-subscribes on a runtime that reserves, as Vulkan does, and fails the launch.
Sourcepub fn workers(&self) -> Option<u32>
pub fn workers(&self) -> Option<u32>
Independent workers to spread across: a CPU runtime’s cores, or a GPU’s compute units.
None where nothing reports one, and a caller keeps its behaviour there rather than guess.
Sourcepub fn units_to_fill(&self) -> Option<u32>
pub fn units_to_fill(&self) -> Option<u32>
Units a launch wants in flight to keep this device busy, or None when the device does
not say how wide it is.
Units, not cubes: a worker hides memory latency behind other units’ requests, so one cube per worker starves it however large the cube, and counting cubes would make the answer depend on a cube size decided after it. A CPU runtime’s units are threads, one per core.
The per-worker figure is a floor no runtime reports. Two measurements agree on it: a shared-memory gemv’s best target was 8192 units, and the decode attention that measured fastest on an M2 Pro ran 256 single-plane cubes, also 8192 units, about 431 per worker.
Sourcepub fn cubes_to_fill(&self, units_per_cube: u32) -> Option<u32>
pub fn cubes_to_fill(&self, units_per_cube: u32) -> Option<u32>
Cubes of units_per_cube units that fill this device, or None when it does not say how
wide it is. A preference: the grid may exceed it.
Sourcepub fn load_values(&self, elem: ElemType) -> usize
pub fn load_values(&self, elem: ElemType) -> usize
How many values of elem one load instruction holds: what a step counts its loads in.
Sourcepub fn vector_ceiling(&self, elem: ElemType) -> usize
pub fn vector_ceiling(&self, elem: ElemType) -> usize
The widest vector this device reads elem in: its IO width over the element’s, capped by
the most components a vector takes. The device’s half of a ceiling: a launch reads at the
microkernel’s, narrowed until the operands admit it.
Sourcepub fn transaction_bytes(&self) -> usize
pub fn transaction_bytes(&self) -> usize
Bytes the memory system moves in one transaction, whatever share of them a read wants:
GPU_TRANSACTION_BYTES on a GPU, CPU_CACHE_LINE_BYTES on a CPU runtime. No runtime
reports one, so it is stated per kind of runtime.
What it bounds is waste: a walk whose units read consecutive addresses is coalesced however many they are, and this decides whether the run they cover per step fills a transaction.
Sourcepub fn staggered_pitch(&self, run: usize, elem: ElemType) -> usize
pub fn staggered_pitch(&self, run: usize, elem: ElemType) -> usize
The distance, in elements, between the starts of consecutive runs of run elements of
elem laid one after another: a whole and odd number of transactions.
Odd because a memory system that picks a channel from low power-of-two address bits sends runs a power of two bytes apart to one channel; an odd step reaches every slot of such an interleave. It costs at most one transaction of padding a run.
Sourcepub fn plane_count_per_cube(&self, working_units: usize, plane_dim: u32) -> u32
pub fn plane_count_per_cube(&self, working_units: usize, plane_dim: u32) -> u32
Planes per cube for a plane-parallel launch of working_units units: on a CPU runtime
capped at the core count, on a GPU a power of two up to eight, and never a cube of no
unit on a device whose unit budget is narrower than its plane.
Sourcepub fn strip_budget(&self, plane_size: usize) -> usize
pub fn strip_budget(&self, plane_size: usize) -> usize
The tallest cube, in planes of plane_size, the device spawns once the plane width has
taken cube_dim.x: the cap on cube_dim.y.
A budget, not a preference. A bound that lowers what the caller asked for would also erase the differences between the values autotune measures, so what a taller cube is worth is measured and only what the device can spawn is stated here.
Whether a cube’s shared memory takes an atomic add on an f32: what a cube-scope
microkernel sums its planes’ partials through.
Sourcepub fn bulk_tensor_copy(&self) -> bool
pub fn bulk_tensor_copy(&self) -> bool
Whether a stage may be filled by the TMA engine: the bulk tensor copy, and the barriers its copies land on.
Sourcepub fn unit_async_copy(&self) -> bool
pub fn unit_async_copy(&self) -> bool
Whether a stage may be filled by the units’ async copies: the copy engine, and the barriers its copies land on.
Sourcepub fn barrier_ops(&self) -> bool
pub fn barrier_ops(&self) -> bool
Whether the runtime carries a barrier type, which is how a stage’s producer and consumer meet when they are different planes.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Hardware
impl RefUnwindSafe for Hardware
impl Send for Hardware
impl Sync for Hardware
impl Unpin for Hardware
impl UnsafeUnpin for Hardware
impl UnwindSafe for Hardware
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneExpand for Twhere
T: Clone,
impl<T> CloneExpand for Twhere
T: Clone,
fn __expand_clone_method(&self, _: &Scope) -> T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§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>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§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>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§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.Source§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.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more