Fix ROCm backend (target MIGraphX EP) and add CUDA support
ROCm silently fell back to CPU: the code targeted ONNX Runtime's classic ROCMExecutionProvider, but distro ROCm-enabled ONNX Runtime builds (e.g. Arch's onnxruntime-rocm) are commonly compiled with --use_migraphx instead, and registration failures were invisible since breadmill never installed a tracing subscriber. Switches the rocm feature to target MIGraphX, adds a default tracing subscriber so EP registration success/failure is always visible, and fixes a real crash where MIGraphX's output sequence padding could index the attention mask out of bounds during mean-pooling. Also adds a CUDA backend (--cuda / backend = "cuda") mirroring the same ort execution-provider pattern, for NVIDIA hardware. Version bump: 0.1.0 -> 0.2.0.
This commit is contained in:
parent
d3843f3131
commit
2618a33fd5
10 changed files with 227 additions and 96 deletions
|
|
@ -19,8 +19,14 @@ pub enum Backend {
|
|||
/// AMD XDNA NPU via the VitisAI ONNX Runtime execution provider.
|
||||
/// `cache_dir` is used to store the compiled NPU model between runs.
|
||||
Npu { cache_dir: PathBuf },
|
||||
/// AMD iGPU via the ROCm ONNX Runtime execution provider.
|
||||
/// AMD iGPU via the MIGraphX ONNX Runtime execution provider (ROCm-backed).
|
||||
/// Distro ROCm ONNX Runtime builds (e.g. Arch's onnxruntime-rocm) are
|
||||
/// commonly compiled with `--use_migraphx`, not `--use_rocm`, so this
|
||||
/// targets `MIGraphXExecutionProvider` rather than the classic
|
||||
/// `ROCMExecutionProvider`.
|
||||
Rocm,
|
||||
/// NVIDIA GPU via the CUDA ONNX Runtime execution provider.
|
||||
Cuda,
|
||||
}
|
||||
|
||||
pub struct OrtEmbedder {
|
||||
|
|
@ -108,11 +114,14 @@ impl OrtEmbedder {
|
|||
let actual_seq = shape[1] as usize;
|
||||
let actual_dim = shape[2] as usize;
|
||||
|
||||
// Mean-pool over non-padding positions
|
||||
// Mean-pool over non-padding positions. Some execution providers (e.g.
|
||||
// MIGraphX) pad the output sequence dimension for kernel efficiency, so
|
||||
// actual_seq can exceed mask.len() — only positions covered by our own
|
||||
// attention mask are meaningful, so cap the loop at whichever is shorter.
|
||||
let mut result = vec![0.0f32; actual_dim];
|
||||
let mut count = 0usize;
|
||||
|
||||
for t in 0..actual_seq {
|
||||
for t in 0..actual_seq.min(mask.len()) {
|
||||
if mask[t] > 0 {
|
||||
for d in 0..actual_dim {
|
||||
result[d] += data[t * actual_dim + d];
|
||||
|
|
@ -155,6 +164,7 @@ fn configure_eps(builder: SessionBuilder, backend: &Backend) -> Result<SessionBu
|
|||
Backend::Cpu => Ok(builder),
|
||||
Backend::Npu { cache_dir } => npu_session(builder, cache_dir),
|
||||
Backend::Rocm => rocm_session(builder),
|
||||
Backend::Cuda => cuda_session(builder),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,14 +205,19 @@ fn build_vitis_ep(cache_dir: &Path) -> Result<ort::ep::ExecutionProviderDispatch
|
|||
.build())
|
||||
}
|
||||
|
||||
// ---- ROCm EP (AMD iGPU) -----------------------------------------------------
|
||||
// ---- MIGraphX EP (AMD iGPU, ROCm-backed) -------------------------------------
|
||||
|
||||
#[cfg(feature = "rocm")]
|
||||
fn rocm_session(builder: SessionBuilder) -> Result<SessionBuilder, String> {
|
||||
eprintln!("breadmill: using ROCm execution provider (device 0)");
|
||||
eprintln!("breadmill: using MIGraphX execution provider (device 0)");
|
||||
eprintln!(
|
||||
"breadmill: note — check the log line above/below for \"Successfully registered \
|
||||
`MIGraphXExecutionProvider`\"; if it's missing, the ONNX Runtime in use wasn't built \
|
||||
with MIGraphX support and inference silently fell back to CPU"
|
||||
);
|
||||
builder
|
||||
.with_execution_providers([
|
||||
ort::execution_providers::ROCmExecutionProvider::default().build(),
|
||||
ort::ep::MIGraphX::default().with_device_id(0).build(),
|
||||
ort::ep::CPU::default().build(),
|
||||
])
|
||||
.map_err(|e| e.to_string())
|
||||
|
|
@ -214,6 +229,30 @@ fn rocm_session(builder: SessionBuilder) -> Result<SessionBuilder, String> {
|
|||
Ok(builder)
|
||||
}
|
||||
|
||||
// ---- CUDA EP (NVIDIA GPU) ----------------------------------------------------
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
fn cuda_session(builder: SessionBuilder) -> Result<SessionBuilder, String> {
|
||||
eprintln!("breadmill: using CUDA execution provider (device 0)");
|
||||
eprintln!(
|
||||
"breadmill: note — check the log line above/below for \"Successfully registered \
|
||||
`CUDAExecutionProvider`\"; if it's missing, the ONNX Runtime in use wasn't built \
|
||||
with CUDA support and inference silently fell back to CPU"
|
||||
);
|
||||
builder
|
||||
.with_execution_providers([
|
||||
ort::ep::CUDA::default().with_device_id(0).build(),
|
||||
ort::ep::CPU::default().build(),
|
||||
])
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "cuda"))]
|
||||
fn cuda_session(builder: SessionBuilder) -> Result<SessionBuilder, String> {
|
||||
eprintln!("breadmill: CUDA backend requested but not compiled in (rebuild with --features cuda); using CPU");
|
||||
Ok(builder)
|
||||
}
|
||||
|
||||
/// Locate the VitisAI EP config file required by the AMD Ryzen AI SDK.
|
||||
///
|
||||
/// Search order:
|
||||
|
|
|
|||
|
|
@ -24,17 +24,27 @@ const TOKENIZER_URL: &str =
|
|||
"https://huggingface.co/nomic-ai/nomic-embed-text-v1.5/resolve/main/tokenizer.json";
|
||||
|
||||
fn main() {
|
||||
// Surfaces ort's EP-registration warnings/errors (e.g. a GPU EP silently
|
||||
// falling back to CPU) by default, without requiring RUST_LOG to be set.
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn,ort=info")),
|
||||
)
|
||||
.init();
|
||||
|
||||
let raw_args: Vec<String> = std::env::args().collect();
|
||||
|
||||
// Extract global flags before command dispatch.
|
||||
let use_npu = raw_args.iter().any(|a| a == "--npu");
|
||||
let use_rocm = raw_args.iter().any(|a| a == "--rocm");
|
||||
let use_cuda = raw_args.iter().any(|a| a == "--cuda");
|
||||
|
||||
// Build a view of argv without backend flags for command matching.
|
||||
let args: Vec<&str> = raw_args
|
||||
.iter()
|
||||
.skip(1)
|
||||
.filter(|a| a.as_str() != "--npu" && a.as_str() != "--rocm")
|
||||
.filter(|a| a.as_str() != "--npu" && a.as_str() != "--rocm" && a.as_str() != "--cuda")
|
||||
.map(|s| s.as_str())
|
||||
.collect();
|
||||
|
||||
|
|
@ -49,7 +59,7 @@ fn main() {
|
|||
}
|
||||
}
|
||||
Some("--reindex") | Some("reindex") => {
|
||||
if let Err(e) = run_daemon(true, use_npu, use_rocm) {
|
||||
if let Err(e) = run_daemon(true, use_npu, use_rocm, use_cuda) {
|
||||
eprintln!("breadmill: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
|
@ -66,7 +76,7 @@ fn main() {
|
|||
cli_status();
|
||||
}
|
||||
None | Some("serve") | Some("--serve") => {
|
||||
if let Err(e) = run_daemon(false, use_npu, use_rocm) {
|
||||
if let Err(e) = run_daemon(false, use_npu, use_rocm, use_cuda) {
|
||||
eprintln!("breadmill: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
|
@ -74,7 +84,7 @@ fn main() {
|
|||
Some(cmd) => {
|
||||
eprintln!("breadmill: unknown command: {}", cmd);
|
||||
eprintln!(
|
||||
"usage: breadmill [serve|reindex|fetch-model|query <text>|status] [--npu|--rocm] [--version]"
|
||||
"usage: breadmill [serve|reindex|fetch-model|query <text>|status] [--npu|--rocm|--cuda] [--version]"
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
|
@ -83,7 +93,7 @@ fn main() {
|
|||
|
||||
// ---- Daemon -----------------------------------------------------------------
|
||||
|
||||
fn run_daemon(force_reindex: bool, use_npu: bool, use_rocm: bool) -> Result<(), String> {
|
||||
fn run_daemon(force_reindex: bool, use_npu: bool, use_rocm: bool, use_cuda: bool) -> Result<(), String> {
|
||||
let config = breadsearch_shared::Config::load();
|
||||
let state_dir = breadsearch_shared::state_dir();
|
||||
let cache_dir = breadsearch_shared::cache_dir();
|
||||
|
|
@ -101,6 +111,9 @@ fn run_daemon(force_reindex: bool, use_npu: bool, use_rocm: bool) -> Result<(),
|
|||
} else if use_rocm || config.model.backend == "rocm" {
|
||||
eprintln!("breadmill: ROCm backend selected");
|
||||
Backend::Rocm
|
||||
} else if use_cuda || config.model.backend == "cuda" {
|
||||
eprintln!("breadmill: CUDA backend selected");
|
||||
Backend::Cuda
|
||||
} else {
|
||||
Backend::Cpu
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue