pub struct NpuDevice { /* private fields */ }Expand description
An explicitly selected NPU ordinal.
Implementations§
Source§impl NpuDevice
impl NpuDevice
Sourcepub unsafe fn load_program(&self, path: impl AsRef<Path>) -> Result<NpuProgram>
pub unsafe fn load_program(&self, path: impl AsRef<Path>) -> Result<NpuProgram>
Load trusted code on this device.
§Safety
The image must satisfy crate::npu::NpuProgram::load’s contract.
Examples found in repository?
examples/shared_roundtrip.rs (line 20)
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}More examples
examples/shared_bench.rs (line 20)
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 79)
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}Trait Implementations§
Auto Trait Implementations§
impl Freeze for NpuDevice
impl RefUnwindSafe for NpuDevice
impl Send for NpuDevice
impl Sync for NpuDevice
impl Unpin for NpuDevice
impl UnsafeUnpin for NpuDevice
impl UnwindSafe for NpuDevice
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