Skip to main content

Kernel

Struct Kernel 

Source
pub struct Kernel { /* private fields */ }
Expand description

Trusted native GPU entry and its declaration-order argument layout.

Implementations§

Source§

impl Kernel

Source

pub fn sanitizer_reports(&self) -> Result<SanitizerReports>

Collect owned sanitizer reports after all native uses have retired. Returns Busy while work is pending and Unsupported for ordinary kernels.

Source

pub fn info(&self) -> &ExportInfo

Checked argument and launch metadata.

Examples found in repository?
examples/gemm_pipeline.rs (line 41)
23fn gpu_kernel(
24    runtime: &Runtime,
25    source: &str,
26    symbol: &str,
27    config: &[(&str, String)],
28    elements: usize,
29    bindings: Vec<BindingContract>,
30) -> Result<GpuKernel> {
31    let compiler = hrx::loom::Compiler::for_target(None, runtime.gpu()?.target())?;
32    let module = compiler.module(source);
33    let mut spec = hrx::loom::Specialization::new(symbol);
34    for (key, value) in config {
35        spec.set_config(*key, value.clone());
36    }
37    let artifact = module.compile(&spec)?;
38    let stream = hrx::gpu::Stream::open()?;
39    let raw = unsafe { stream.load_artifact(&artifact) }?;
40    let mut constants = hrx::gpu::Constants::new();
41    match raw.info().constant_byte_length {
42        8 => constants.push(elements as u32)?,
43        12 => constants.push(elements as u64)?,
44        _ => return Err(hrx::Error::Message("unexpected scalar ABI".into())),
45    }
46    constants.push(1f32)?;
47    unsafe {
48        runtime.load_gpu_kernel(
49            artifact.path(),
50            symbol,
51            [elements.div_ceil(256) as u32, 1, 1],
52            [256, 1, 1],
53            KernelContract {
54                bindings,
55                constants: constants.as_bytes().to_vec(),
56            },
57        )
58    }
59}
More examples
Hide additional examples
examples/stream_bench.rs (line 90)
30fn main() -> Result<()> {
31    let mut stream = Stream::open()?;
32    let mut metrics = BTreeMap::new();
33    let samples = std::env::var("HRX_BENCH_SAMPLES")
34        .ok()
35        .and_then(|value| value.parse::<usize>().ok())
36        .filter(|&value| value > 0)
37        .unwrap_or(9);
38    let (ns, _) = measure(&mut stream, samples, 50_000, |stream| {
39        for _ in 0..50_000 {
40            let buffer = stream.scratch(1024 * 1024)?;
41            black_box(&buffer);
42            stream.recycle(buffer)?;
43        }
44        Ok(())
45    })?;
46    metrics.insert("scratch_reuse_ns", ns);
47    let (ns, _) = measure(&mut stream, samples, 2_000, |stream| {
48        for _ in 0..2_000 {
49            black_box(stream.allocate(1024 * 1024)?);
50        }
51        Ok(())
52    })?;
53    metrics.insert("allocate_drop_ns", ns);
54
55    let bytes = 256 * 1024 * 1024;
56    let chunk = vec![0x3cu8; 4 * 1024 * 1024];
57    let weights = stream.allocate(bytes)?;
58    let (_, ns) = measure(&mut stream, samples, 1, |stream| {
59        for offset in (0..bytes).step_by(chunk.len()) {
60            stream.upload(weights.binding().slice(offset, chunk.len())?, &chunk)?;
61        }
62        Ok(())
63    })?;
64    metrics.insert(
65        "queued_upload_gib_s",
66        bytes as f64 / (1024f64.powi(3) * ns * 1e-9),
67    );
68    let mut check = [0u8; 16];
69    stream.read_blocking(
70        weights.try_slice(bytes - check.len(), check.len())?,
71        &mut check,
72    )?;
73    assert_eq!(check, [0x3c; 16]);
74    drop(weights);
75
76    let compiler = hrx::loom::Compiler::for_stream(None, &stream)?;
77    let module = compiler.module(include_str!("../tests/kernels/euler.loom"));
78    let mut spec = hrx::loom::Specialization::new("krea2_euler");
79    spec.set_config("krea2.euler.grid_x", "1");
80    spec.set_config("krea2.euler.grid_y", "1");
81    let artifact = module.compile(&spec)?;
82    // This repository owns the source; use its exact dimensions and scalar layout.
83    let kernel = unsafe { stream.load_artifact(&artifact)? };
84    let sample = stream.allocate(512)?;
85    let velocity = stream.allocate(512)?;
86    let ones: Vec<u8> = (0..256).flat_map(|_| 0x3f80u16.to_le_bytes()).collect();
87    stream.upload_blocking(sample.binding(), &ones)?;
88    stream.upload_blocking(velocity.binding(), &ones)?;
89    let mut constants = Constants::new();
90    match kernel.info().constant_byte_length {
91        8 => constants.push(256u32)?,
92        12 => constants.push(256u64)?,
93        n => return Err(hrx::Error::Message(format!("unexpected constant size {n}"))),
94    }
95    constants.push(0f32)?;
96    let bindings = [sample.binding(), velocity.binding()];
97    let (enqueue, complete) = measure(&mut stream, samples, 2048, |stream| {
98        for _ in 0..2048 {
99            // The source accesses exactly the 256 bf16 elements in each buffer.
100            unsafe {
101                stream.dispatch(&kernel, [1; 3], [256, 1, 1], &constants, &bindings)?;
102            }
103        }
104        Ok(())
105    })?;
106    metrics.insert("dispatch_enqueue_ns", enqueue);
107    metrics.insert("dispatch_complete_ns", complete);
108
109    // Use disjoint allocations in both graphs. Even an idempotent store is a
110    // write: concurrent Euler dispatches cannot share their in/out buffer.
111    let mut graph_buffers = Vec::new();
112    for _ in 0..32 {
113        let sample = stream.allocate(512)?;
114        let velocity = stream.allocate(512)?;
115        stream.upload(sample.binding(), &ones)?;
116        stream.upload(velocity.binding(), &ones)?;
117        graph_buffers.push((sample, velocity));
118    }
119    stream.synchronize()?;
120    let mut builder = stream.graph()?;
121    let mut previous = None;
122    for (sample, velocity) in &graph_buffers {
123        let after = previous.as_slice();
124        previous = Some(unsafe {
125            builder.dispatch(
126                after,
127                &kernel,
128                [1; 3],
129                [256, 1, 1],
130                &constants,
131                &[sample.binding(), velocity.binding()],
132            )?
133        });
134    }
135    let chained = builder.finish()?;
136
137    // The same disjoint workloads, this time allowing concurrent scheduling.
138    let mut builder = stream.graph()?;
139    for (sample, velocity) in &graph_buffers {
140        unsafe {
141            builder.dispatch(
142                &[],
143                &kernel,
144                [1; 3],
145                [256, 1, 1],
146                &constants,
147                &[sample.binding(), velocity.binding()],
148            )?;
149        }
150    }
151    let independent = builder.finish()?;
152    let mut graphs = [chained, independent];
153    let mut enqueue = [Vec::new(), Vec::new()];
154    let mut complete = [Vec::new(), Vec::new()];
155    for round in 0..samples + 3 {
156        for arm in if round % 2 == 0 { [0, 1] } else { [1, 0] } {
157            stream.synchronize()?;
158            let start = Instant::now();
159            for _ in 0..256 {
160                stream.launch(&mut graphs[arm])?;
161            }
162            let submitted = start.elapsed();
163            stream.synchronize()?;
164            let elapsed = start.elapsed();
165            if round >= 3 {
166                enqueue[arm].push(submitted.as_secs_f64() * 1e9 / 256.0);
167                complete[arm].push(elapsed.as_secs_f64() * 1e9 / (256.0 * 32.0));
168            }
169        }
170    }
171    for values in enqueue.iter_mut().chain(complete.iter_mut()) {
172        values.sort_by(f64::total_cmp);
173    }
174    let ns = complete[0][samples / 2];
175    let ns_free = complete[1][samples / 2];
176    metrics.insert("graph_enqueue_ns_per_replay", enqueue[0][samples / 2]);
177    metrics.insert("graph_complete_ns_per_kernel", ns);
178    metrics.insert("graph_independent_ns_per_kernel", ns_free);
179    metrics.insert("graph_scheduling_difference_ns_per_kernel", ns - ns_free);
180    for (sample, _) in &graph_buffers {
181        let mut output = vec![0; 512];
182        stream.read_blocking(sample.binding(), &mut output)?;
183        assert_eq!(output, ones);
184    }
185    let mut output = vec![0; 512];
186    stream.read_blocking(sample.binding(), &mut output)?;
187    assert_eq!(output, ones);
188    println!("{}", serde_json::to_string(&metrics)?);
189    Ok(())
190}
Source

pub fn symbol(&self) -> &str

Selected native entry name.

Source

pub fn launch_program(&self) -> Result<LaunchProgram>

Create an independent evaluator of this artifact’s exact export.

Source

pub fn launch_config( &self, workload_argument_bits: &[u64], ) -> Result<LaunchConfig>

Evaluate and validate compiler launch geometry during preparation.

Trait Implementations§

Source§

impl Clone for Kernel

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Kernel

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DropFlavorWrapper<T> for T

Source§

type Flavor = MayDrop

The DropFlavor that wraps T into Self
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, W> HasTypeWitness<W> for T
where W: MakeTypeWitness<Arg = T>, T: ?Sized,

Source§

const WITNESS: W = W::MAKE

A constant of the type witness
Source§

impl<T> Identity for T
where T: ?Sized,

Source§

const TYPE_EQ: TypeEq<T, <T as Identity>::Type> = TypeEq::NEW

Proof that Self is the same type as Self::Type, provides methods for casting between Self and Self::Type.
Source§

type Type = T

The same type as Self, used to emulate type equality bounds (T == U) with associated type equality constraints (T: Identity<Type = U>).
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<E> ResultError for E
where E: Send + Debug + Sync,

Source§

impl<T> ResultType for T
where T: Send + Clone + Sync + Debug,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more