Keep the embedder off the Hexagon, and let the probe example ask for a runtime
On the tablet the engine compiled arcface for the NPU: the routing compared the form a rung wants with the form on offer, and for the embedder both are f32, so nothing said no. A rung now says which roles it serves at all, and the Hexagon does not serve the embedder (§7 — its vectors must compare across devices). Tested at the routing seam. dr-segment's onnx_probe example still named ort-tract, which is what stopped the workspace test build.
This commit is contained in:
@@ -55,6 +55,7 @@ pub fn run() {
|
|||||||
let mut jobs: Vec<(crate::Role, Source, u64)> = cfg
|
let mut jobs: Vec<(crate::Role, Source, u64)> = cfg
|
||||||
.models
|
.models
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|(role, _)| rung.serves(*role))
|
||||||
.filter_map(|(role, path)| {
|
.filter_map(|(role, path)| {
|
||||||
let (path, form) = crate::resolve_model(*role, path);
|
let (path, form) = crate::resolve_model(*role, path);
|
||||||
(form == rung.form(*role)).then(|| {
|
(form == rung.form(*role)).then(|| {
|
||||||
@@ -65,7 +66,7 @@ pub fn run() {
|
|||||||
.chain(cfg.embedded.iter().filter_map(|(role, bytes)| {
|
.chain(cfg.embedded.iter().filter_map(|(role, bytes)| {
|
||||||
// An embedded model has no int8 sibling to offer a rung that
|
// An embedded model has no int8 sibling to offer a rung that
|
||||||
// wants one; it runs on that rung's fallback.
|
// wants one; it runs on that rung's fallback.
|
||||||
(rung.form(*role) == Form::F32).then_some((
|
(rung.serves(*role) && rung.form(*role) == Form::F32).then_some((
|
||||||
*role,
|
*role,
|
||||||
Source::Bytes(bytes),
|
Source::Bytes(bytes),
|
||||||
bytes.len() as u64,
|
bytes.len() as u64,
|
||||||
|
|||||||
@@ -95,12 +95,21 @@ impl Rung {
|
|||||||
|
|
||||||
/// The model form this rung wants for a role.
|
/// The model form this rung wants for a role.
|
||||||
fn form(self, role: Role) -> Form {
|
fn form(self, role: Role) -> Form {
|
||||||
match (self, role) {
|
match self {
|
||||||
(Rung::Hexagon, Role::Embedder) => Form::F32,
|
Rung::Hexagon => Form::Int8,
|
||||||
(Rung::Hexagon, _) => Form::Int8,
|
|
||||||
_ => Form::F32,
|
_ => Form::F32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this rung runs `role` at all. The Hexagon takes int8 graphs
|
||||||
|
/// only, and the embedder is never int8 (§7) — it runs on the CPU
|
||||||
|
/// beside a detector on the NPU, so its vectors compare across devices.
|
||||||
|
fn serves(self, role: Role) -> bool {
|
||||||
|
match self {
|
||||||
|
Rung::Hexagon => role != Role::Embedder,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How long a session outlives its last use unless [`Config::decay`] says
|
/// How long a session outlives its last use unless [`Config::decay`] says
|
||||||
@@ -407,7 +416,7 @@ fn current_rung(s: &State) -> Rung {
|
|||||||
/// caller adds [`form_suffix`] to the `model_id` it records.
|
/// caller adds [`form_suffix`] to the `model_id` it records.
|
||||||
pub fn resolve_model(role: Role, canonical: &Path) -> (PathBuf, Form) {
|
pub fn resolve_model(role: Role, canonical: &Path) -> (PathBuf, Form) {
|
||||||
let rung = current_rung(&state().lock().unwrap());
|
let rung = current_rung(&state().lock().unwrap());
|
||||||
if rung.form(role) == Form::Int8 {
|
if rung.serves(role) && rung.form(role) == Form::Int8 {
|
||||||
let sibling = int8_sibling(canonical);
|
let sibling = int8_sibling(canonical);
|
||||||
if sibling.is_file() {
|
if sibling.is_file() {
|
||||||
return (sibling, Form::Int8);
|
return (sibling, Form::Int8);
|
||||||
@@ -451,7 +460,7 @@ pub fn open(role: Role, form: Form, bytes: &[u8]) -> Result<Model, Error> {
|
|||||||
/// the form on offer, or a missing engine says one lower (§6 step 4).
|
/// the form on offer, or a missing engine says one lower (§6 step 4).
|
||||||
fn effective_rung(s: &State, selected: Rung, role: Role, form: Form, bytes: &[u8]) -> Rung {
|
fn effective_rung(s: &State, selected: Rung, role: Role, form: Form, bytes: &[u8]) -> Rung {
|
||||||
let mut rung = selected;
|
let mut rung = selected;
|
||||||
if rung.form(role) != form {
|
if !rung.serves(role) || rung.form(role) != form {
|
||||||
// The embedder on a Hexagon device, or an f32 detector where the int8
|
// The embedder on a Hexagon device, or an f32 detector where the int8
|
||||||
// sibling was missing: neither can go to the NPU.
|
// sibling was missing: neither can go to the NPU.
|
||||||
rung = rung.fallback();
|
rung = rung.fallback();
|
||||||
@@ -541,6 +550,36 @@ mod tests {
|
|||||||
assert_eq!(loaded(), 1);
|
assert_eq!(loaded(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_hexagon_never_takes_the_embedder() {
|
||||||
|
assert!(!Rung::Hexagon.serves(Role::Embedder));
|
||||||
|
assert!(Rung::Hexagon.serves(Role::Detector));
|
||||||
|
assert_eq!(Rung::Hexagon.form(Role::Detector), Form::Int8);
|
||||||
|
// A detector offered in f32 on a Hexagon device lands on the CPU.
|
||||||
|
let s = State {
|
||||||
|
config: Config::default(),
|
||||||
|
cache: Cache {
|
||||||
|
rung: Some(Rung::Hexagon),
|
||||||
|
..Cache::default()
|
||||||
|
},
|
||||||
|
probing: false,
|
||||||
|
wanted: 0,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
effective_rung(&s, Rung::Hexagon, Role::Embedder, Form::F32, b""),
|
||||||
|
Rung::Cpu
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
effective_rung(&s, Rung::Hexagon, Role::Detector, Form::F32, b""),
|
||||||
|
Rung::Cpu
|
||||||
|
);
|
||||||
|
// An int8 detector whose context is not compiled yet: also the CPU.
|
||||||
|
assert_eq!(
|
||||||
|
effective_rung(&s, Rung::Hexagon, Role::Detector, Form::Int8, b""),
|
||||||
|
Rung::Cpu
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_status_line_reads_as_the_floor_before_init() {
|
fn the_status_line_reads_as_the_floor_before_init() {
|
||||||
let s = status();
|
let s = status();
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ ndarray = { workspace = true, optional = true }
|
|||||||
# tree for embedded previews.
|
# tree for embedded previews.
|
||||||
zune-jpeg.workspace = true
|
zune-jpeg.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
# The probe example drives `ort` directly to print the raw load error, and
|
||||||
|
# asks the engine for a runtime by name rather than naming one itself.
|
||||||
|
dr-inference-engine.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# On by default: a local adjustment that cannot select a subject is half the
|
# On by default: a local adjustment that cannot select a subject is half the
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ fn main() {
|
|||||||
let bytes = std::fs::read(&path).expect("read model");
|
let bytes = std::fs::read(&path).expect("read model");
|
||||||
println!("{path}: {} bytes", bytes.len());
|
println!("{path}: {} bytes", bytes.len());
|
||||||
|
|
||||||
let _ = ort::set_api(ort_tract::api());
|
dr_inference_engine::ensure_runtime();
|
||||||
let t = Instant::now();
|
let t = Instant::now();
|
||||||
let mut session =
|
let mut session =
|
||||||
match ort::session::Session::builder().and_then(|mut b| b.commit_from_memory(&bytes)) {
|
match ort::session::Session::builder().and_then(|mut b| b.commit_from_memory(&bytes)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user