diff --git a/crates/engine-gpu/src/lib.rs b/crates/engine-gpu/src/lib.rs index 9a784aa..93bd20c 100644 --- a/crates/engine-gpu/src/lib.rs +++ b/crates/engine-gpu/src/lib.rs @@ -281,6 +281,14 @@ impl GpuEngine { pub fn device_count(&self) -> usize { self.contexts.len() } + + /// Explicitly clear thread-local GPU resources. + /// Call this before thread exit to avoid TLS destruction order issues with wgpu. + pub fn clear_worker_resources() { + WORKER_RESOURCES.with(|resources| { + *resources.borrow_mut() = None; + }); + } } impl MinerEngine for GpuEngine { diff --git a/crates/miner-cli/Cargo.toml b/crates/miner-cli/Cargo.toml index 2e1358c..c2bacd8 100644 --- a/crates/miner-cli/Cargo.toml +++ b/crates/miner-cli/Cargo.toml @@ -6,7 +6,7 @@ publish = false description = "CLI binary to run the Quantus External Miner service" [features] -default = [] +default = ["gpu"] gpu = ["miner-service/gpu"] [dependencies] diff --git a/crates/miner-cli/src/main.rs b/crates/miner-cli/src/main.rs index e5eb261..e4c942b 100644 --- a/crates/miner-cli/src/main.rs +++ b/crates/miner-cli/src/main.rs @@ -402,8 +402,29 @@ async fn run_benchmark_command( // Chunk sizes let cpu_chunk = 10_000u64; + // Increase GPU chunk size to amortize submission overhead (10M) + // 1M might be too small for fast GPUs, 100M might be too large for short benchmarks. let gpu_chunk = 1_000_000u64; + if effective_gpu_workers > 0 { + if let Some(gpu_engine_arc) = &gpu_engine { + // We can't easily get device count from MinerEngine trait without downcasting or extending trait. + // But we know it's GpuEngine here if feature=gpu is enabled. + #[cfg(feature = "gpu")] + { + if let Some(gpu_ptr) = gpu_engine_arc.as_any().downcast_ref::() { + let device_count = gpu_ptr.device_count(); + if effective_gpu_workers > device_count { + println!("⚠️ WARNING: You have requested {} GPU workers for {} device(s).", effective_gpu_workers, device_count); + println!(" Multiple workers per device creates contention and typically reduces hashrate."); + println!(" Recommended: 1 worker per GPU device."); + println!(); + } + } + } + } + } + for worker_id in 0..total_workers { // Determine type and engine let (engine, nonces_per_worker) = if worker_id < effective_cpu_workers { @@ -487,6 +508,10 @@ async fn run_benchmark_command( } } + // Cleanup TLS resources explicitly to avoid panic on exit + #[cfg(feature = "gpu")] + engine_gpu::GpuEngine::clear_worker_resources(); + worker_hashes }); diff --git a/crates/miner-service/Cargo.toml b/crates/miner-service/Cargo.toml index ac8f33d..89b19b3 100644 --- a/crates/miner-service/Cargo.toml +++ b/crates/miner-service/Cargo.toml @@ -6,7 +6,7 @@ publish = false description = "Service layer: HTTP API, job orchestration, and engine abstraction for the Quantus External Miner" [features] -default = ["cpu", "metrics"] +default = ["cpu", "gpu", "metrics"] # Enable CPU engine by default. cpu = ["engine-cpu"] # Enable GPU engine. diff --git a/crates/miner-service/src/lib.rs b/crates/miner-service/src/lib.rs index 7bd18b4..49ace5d 100644 --- a/crates/miner-service/src/lib.rs +++ b/crates/miner-service/src/lib.rs @@ -933,6 +933,12 @@ fn mine_range_with_engine_typed( } } + // Explicitly clear thread-local GPU resources to avoid TLS order panic + #[cfg(feature = "gpu")] + if engine_type == "GPU" { + engine_gpu::GpuEngine::clear_worker_resources(); + } + log::debug!(target: "miner", "Job {job_id} thread {thread_id} completed."); }