Skip to main content

NpuDevice

Struct NpuDevice 

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

An explicitly selected NPU ordinal.

Implementations§

Source§

impl NpuDevice

Source

pub fn index(&self) -> i32

Physical NPU ordinal.

Source

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
Hide additional 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§

Source§

impl Clone for NpuDevice

Source§

fn clone(&self) -> NpuDevice

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

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<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