pub struct ExecutableGraph { /* private fields */ }Expand description
A prepared graph with fixed bindings and reusable bounded submission slots.
Implementations§
Source§impl ExecutableGraph
impl ExecutableGraph
Sourcepub fn submit(&self) -> Result<Completion>
pub fn submit(&self) -> Result<Completion>
Enqueue without waiting for a device. Conflicting prior work is ordered.
Busy indicates live host mappings or exhausted pending/completion slots.
Examples found in repository?
examples/graph_prepare.rs (line 29)
4fn main() -> hrx::Result<()> {
5 let runtime = Runtime::new()?;
6 let buffer = runtime.allocate(4096, MemoryPlacement::GpuLocal)?;
7 let stream = Stream::open()?;
8 let raw = stream.allocate(4096)?;
9 for count in [128, 512, 1024, 2048] {
10 let begin = Instant::now();
11 let mut graph = runtime.graph();
12 for _ in 0..count {
13 graph.fill(buffer.view(), 0)?;
14 }
15 let graph = graph.prepare()?;
16 let inferred = begin.elapsed();
17 let begin = Instant::now();
18 let mut native = stream.graph()?;
19 let mut last = None;
20 for _ in 0..count {
21 last = Some(native.fill(last.as_slice(), raw.binding(), 0)?);
22 }
23 let _native = native.finish()?;
24 println!(
25 "nodes={count} inferred_us={} explicit_us={}",
26 inferred.as_micros(),
27 begin.elapsed().as_micros()
28 );
29 graph.submit()?.wait()?;
30 }
31 Ok(())
32}More examples
examples/shared_roundtrip.rs (line 46)
8fn main() -> Result<()> {
9 let args: Vec<_> = std::env::args().collect();
10 if args.len() != 4 {
11 return Err(hrx::Error::Message(
12 "usage: shared_roundtrip <passthrough.xclbin> <instructions.bin> <bytes>".into(),
13 ));
14 }
15 let bytes = args[3]
16 .parse::<usize>()
17 .map_err(|e| hrx::Error::Message(e.to_string()))?;
18 let runtime = Runtime::new()?;
19 // This example accepts trusted local artifacts with the documented ABI.
20 let program = unsafe { runtime.npu(0)?.load_program(&args[1]) }?;
21 let binding = |bytes, access| BindingContract {
22 bytes,
23 access,
24 alignment: 4,
25 layout: "u32 passthrough".into(),
26 };
27 let contract = KernelContract {
28 bindings: vec![
29 binding(bytes, Access::Read),
30 binding(4096, Access::Read),
31 binding(bytes, Access::Write),
32 ],
33 constants: vec![],
34 };
35 let kernel = unsafe { program.kernel(&std::fs::read(&args[2])?, contract) }?;
36 let a = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
37 let b = runtime.allocate(4096, MemoryPlacement::Shared(program.clone()))?;
38 let c = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
39 let result = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
40 let mut graph = runtime.graph();
41 graph.fill(a.view(), 0x5a)?;
42 graph.npu(&kernel, &[a.view(), b.view(), c.view()])?;
43 graph.copy(result.view(), c.view())?;
44 let graph = graph.prepare()?;
45 for _ in 0..10 {
46 graph.submit()?.wait()?;
47 let mapped = result.map_read()?;
48 if mapped.iter().any(|&b| b != 0x5a) {
49 return Err(hrx::Error::Message("GPU/NPU roundtrip mismatch".into()));
50 }
51 }
52 println!(
53 "GPU -> NPU -> GPU: all {bytes} bytes correct, 10 executions, {} prepared regions",
54 graph.regions()
55 );
56 Ok(())
57}examples/shared_bench.rs (line 48)
9fn main() -> Result<()> {
10 let args: Vec<_> = std::env::args().collect();
11 if args.len() != 4 {
12 return Err(hrx::Error::Message(
13 "usage: shared_bench <passthrough.xclbin> <instructions.bin> <bytes>".into(),
14 ));
15 }
16 let bytes = args[3]
17 .parse::<usize>()
18 .map_err(|e| hrx::Error::Message(e.to_string()))?;
19 let runtime = Runtime::new()?;
20 let program = unsafe { runtime.npu(0)?.load_program(&args[1]) }?;
21 let binding = |bytes, access| BindingContract {
22 bytes,
23 access,
24 alignment: 4,
25 layout: "passthrough".into(),
26 };
27 let kernel = unsafe {
28 program.kernel(
29 &std::fs::read(&args[2])?,
30 KernelContract {
31 bindings: vec![
32 binding(bytes, Access::Read),
33 binding(4096, Access::Read),
34 binding(bytes, Access::Write),
35 ],
36 constants: vec![],
37 },
38 )
39 }?;
40 let a = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
41 let b = runtime.allocate(4096, MemoryPlacement::Shared(program.clone()))?;
42 let c = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
43 let mut graph = runtime.graph();
44 graph.fill(a.view(), 0x35)?;
45 graph.npu(&kernel, &[a.view(), b.view(), c.view()])?;
46 let graph = graph.prepare()?;
47 for _ in 0..5 {
48 graph.submit()?.wait()?;
49 }
50 let mut samples = Vec::new();
51 for _ in 0..100 {
52 let start = Instant::now();
53 graph.submit()?.wait()?;
54 samples.push(start.elapsed().as_secs_f64());
55 }
56 if c.map_read()?.iter().any(|&b| b != 0x35) {
57 return Err(hrx::Error::Message("incorrect benchmark output".into()));
58 }
59 samples.sort_by(f64::total_cmp);
60 println!(
61 "bytes={bytes} p50_us={:.2} p95_us={:.2}",
62 percentile(&samples, 50) * 1e6,
63 percentile(&samples, 95) * 1e6
64 );
65 Ok(())
66}examples/gemm_pipeline.rs (line 136)
58fn main() -> Result<()> {
59 let args: Vec<_> = std::env::args().collect();
60 if args.len() != 6 {
61 return Err(hrx::Error::Message(
62 "usage: gemm_pipeline <row-major-bf16.xclbin> <instructions.bin> M K N".into(),
63 ));
64 }
65 let parse = |i: usize| {
66 args[i]
67 .parse::<usize>()
68 .map_err(|e| hrx::Error::Message(e.to_string()))
69 };
70 let (m, k, n) = (parse(3)?, parse(4)?, parse(5)?);
71 let extent = |a: usize, b: usize, width: usize| {
72 a.checked_mul(b)
73 .filter(|&count| count > 0 && count <= (1 << 30))
74 .and_then(|count| count.checked_mul(width))
75 .ok_or_else(|| hrx::Error::Message("invalid matrix extent".into()))
76 };
77 let (a_bytes, b_bytes, c_bytes) = (extent(m, k, 2)?, extent(k, n, 2)?, extent(m, n, 4)?);
78 let runtime = Runtime::new()?;
79 let program = unsafe { runtime.npu(0)?.load_program(&args[1]) }?;
80 let npu = unsafe {
81 program.kernel(
82 &std::fs::read(&args[2])?,
83 KernelContract {
84 bindings: vec![
85 binding(a_bytes, Access::Read),
86 binding(b_bytes, Access::Read),
87 binding(c_bytes, Access::Write),
88 ],
89 constants: vec![],
90 },
91 )
92 }?;
93 let prep = gpu_kernel(
94 &runtime,
95 include_str!("../tests/kernels/euler.loom"),
96 "krea2_euler",
97 &[
98 ("krea2.euler.grid_x", (m * k).div_ceil(256).to_string()),
99 ("krea2.euler.grid_y", "1".into()),
100 ],
101 m * k,
102 vec![
103 binding(a_bytes, Access::ReadWrite),
104 binding(a_bytes, Access::Read),
105 ],
106 )?;
107 let epilogue = gpu_kernel(
108 &runtime,
109 include_str!("../tests/kernels/add_f32.loom"),
110 "add_f32",
111 &[("add.grid", (m * n).div_ceil(256).to_string())],
112 m * n,
113 vec![binding(c_bytes, Access::ReadWrite)],
114 )?;
115 let allocate = |bytes| runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()));
116 let weights = allocate(b_bytes)?;
117 let ones = allocate(a_bytes)?;
118 for buffer in [&weights, &ones] {
119 for word in buffer.map_write()?.as_chunks_mut::<2>().0 {
120 word.copy_from_slice(&0x3f80u16.to_le_bytes());
121 }
122 }
123 let prepare = || -> Result<(ExecutableGraph, Buffer)> {
124 let a = allocate(a_bytes)?;
125 let c = allocate(c_bytes)?;
126 let mut graph = runtime.graph();
127 graph.fill(a.view(), 0)?;
128 graph.gpu(&prep, &[a.view(), ones.view()])?;
129 graph.npu(&npu, &[a.view(), weights.view(), c.view()])?;
130 graph.gpu(&epilogue, &[c.view()])?;
131 Ok((graph.prepare()?, c))
132 };
133 let (first, c0) = prepare()?;
134 let (second, c1) = prepare()?;
135 for _ in 0..5 {
136 first.submit()?.wait()?;
137 second.submit()?.wait()?;
138 }
139 let before = runtime.statistics();
140 let mut latency = Vec::with_capacity(50);
141 for _ in 0..50 {
142 let start = Instant::now();
143 first.submit()?.wait()?;
144 latency.push(start.elapsed().as_secs_f64());
145 }
146 let start = Instant::now();
147 for _ in 0..50 {
148 let a = first.submit()?;
149 let b = second.submit()?;
150 a.wait()?;
151 b.wait()?;
152 }
153 let requests_per_second = 100.0 / start.elapsed().as_secs_f64();
154 for c in [&c0, &c1] {
155 if c.map_read()?
156 .as_chunks::<4>()
157 .0
158 .iter()
159 .any(|v| f32::from_le_bytes(*v) != k as f32 + 1.0)
160 {
161 return Err(hrx::Error::Message("GEMM pipeline output mismatch".into()));
162 }
163 }
164 let after = runtime.statistics();
165 assert_eq!(before.allocations, after.allocations);
166 assert_eq!(before.imports, after.imports);
167 assert_eq!(after.copied_bytes, 0);
168 latency.sort_by(f64::total_cmp);
169 println!(
170 "M={m} K={k} N={n} p50_ms={:.3} p95_ms={:.3} pipelined_requests_s={requests_per_second:.2}",
171 percentile(&latency, 50) * 1e3,
172 percentile(&latency, 95) * 1e3
173 );
174 println!("{}", serde_json::to_string(&after)?);
175 Ok(())
176}Sourcepub fn submit_after(&self, dependencies: &[Completion]) -> Result<Completion>
pub fn submit_after(&self, dependencies: &[Completion]) -> Result<Completion>
Submit after successful producers in this runtime. Failed or cancelled producers prevent execution, even when no memory range overlaps.
Sourcepub fn regions(&self) -> usize
pub fn regions(&self) -> usize
Number of prepared device regions (GPU regions may contain many kernels).
Examples found in repository?
examples/shared_roundtrip.rs (line 54)
8fn main() -> Result<()> {
9 let args: Vec<_> = std::env::args().collect();
10 if args.len() != 4 {
11 return Err(hrx::Error::Message(
12 "usage: shared_roundtrip <passthrough.xclbin> <instructions.bin> <bytes>".into(),
13 ));
14 }
15 let bytes = args[3]
16 .parse::<usize>()
17 .map_err(|e| hrx::Error::Message(e.to_string()))?;
18 let runtime = Runtime::new()?;
19 // This example accepts trusted local artifacts with the documented ABI.
20 let program = unsafe { runtime.npu(0)?.load_program(&args[1]) }?;
21 let binding = |bytes, access| BindingContract {
22 bytes,
23 access,
24 alignment: 4,
25 layout: "u32 passthrough".into(),
26 };
27 let contract = KernelContract {
28 bindings: vec![
29 binding(bytes, Access::Read),
30 binding(4096, Access::Read),
31 binding(bytes, Access::Write),
32 ],
33 constants: vec![],
34 };
35 let kernel = unsafe { program.kernel(&std::fs::read(&args[2])?, contract) }?;
36 let a = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
37 let b = runtime.allocate(4096, MemoryPlacement::Shared(program.clone()))?;
38 let c = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
39 let result = runtime.allocate(bytes, MemoryPlacement::Shared(program.clone()))?;
40 let mut graph = runtime.graph();
41 graph.fill(a.view(), 0x5a)?;
42 graph.npu(&kernel, &[a.view(), b.view(), c.view()])?;
43 graph.copy(result.view(), c.view())?;
44 let graph = graph.prepare()?;
45 for _ in 0..10 {
46 graph.submit()?.wait()?;
47 let mapped = result.map_read()?;
48 if mapped.iter().any(|&b| b != 0x5a) {
49 return Err(hrx::Error::Message("GPU/NPU roundtrip mismatch".into()));
50 }
51 }
52 println!(
53 "GPU -> NPU -> GPU: all {bytes} bytes correct, 10 executions, {} prepared regions",
54 graph.regions()
55 );
56 Ok(())
57}Trait Implementations§
Source§impl Clone for ExecutableGraph
impl Clone for ExecutableGraph
Source§fn clone(&self) -> ExecutableGraph
fn clone(&self) -> ExecutableGraph
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for ExecutableGraph
impl !UnwindSafe for ExecutableGraph
impl Freeze for ExecutableGraph
impl Send for ExecutableGraph
impl Sync for ExecutableGraph
impl Unpin for ExecutableGraph
impl UnsafeUnpin for ExecutableGraph
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
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> DropFlavorWrapper<T> for T
impl<T> DropFlavorWrapper<T> for T
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
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> ⓘ
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 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> ⓘ
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