support multiple gpus
This commit is contained in:
@@ -89,6 +89,9 @@ pub trait MinerEngine: Send + Sync {
|
||||
/// - Return `Exhausted` if the range is fully searched without a solution.
|
||||
/// - Return `Cancelled` if `cancel` was observed during the search.
|
||||
fn search_range(&self, ctx: &JobContext, range: Range, cancel: &AtomicBool) -> EngineStatus;
|
||||
|
||||
/// Enable downcasting to concrete engine types.
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
}
|
||||
|
||||
/// Baseline CPU engine.
|
||||
@@ -113,6 +116,10 @@ impl MinerEngine for BaselineCpuEngine {
|
||||
JobContext::new(header_hash, difficulty)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn search_range(&self, ctx: &JobContext, range: Range, cancel: &AtomicBool) -> EngineStatus {
|
||||
// Ensure start <= end (inclusive range). If not, treat as exhausted.
|
||||
if range.start > range.end {
|
||||
@@ -183,6 +190,10 @@ impl MinerEngine for FastCpuEngine {
|
||||
JobContext::new(header_hash, difficulty)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn search_range(&self, ctx: &JobContext, range: Range, cancel: &AtomicBool) -> EngineStatus {
|
||||
use pow_core::{hash_from_nonce, is_valid_hash, step_nonce};
|
||||
|
||||
@@ -274,6 +285,10 @@ impl MinerEngine for ChainManipulatorEngine {
|
||||
ctx
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn search_range(&self, ctx: &JobContext, range: Range, cancel: &AtomicBool) -> EngineStatus {
|
||||
use pow_core::{hash_from_nonce, is_valid_hash, step_nonce};
|
||||
|
||||
|
||||
@@ -5,8 +5,11 @@ use engine_cpu::{Candidate, EngineStatus, FoundOrigin, MinerEngine, Range};
|
||||
use futures::executor::block_on;
|
||||
use pow_core::JobContext;
|
||||
use primitive_types::U512;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
/// Represents a single GPU device context.
|
||||
struct GpuContext {
|
||||
@@ -28,6 +31,12 @@ struct GpuContext {
|
||||
|
||||
pub struct GpuEngine {
|
||||
contexts: Vec<Arc<GpuContext>>,
|
||||
device_counter: AtomicUsize,
|
||||
}
|
||||
|
||||
// Thread-local storage for consistent GPU device assignment per worker thread
|
||||
thread_local! {
|
||||
static ASSIGNED_GPU_DEVICE: RefCell<Option<usize>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
impl Default for GpuEngine {
|
||||
@@ -252,7 +261,15 @@ impl GpuEngine {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { contexts })
|
||||
Ok(Self {
|
||||
contexts,
|
||||
device_counter: AtomicUsize::new(0),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the number of GPU devices available
|
||||
pub fn device_count(&self) -> usize {
|
||||
self.contexts.len()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +282,10 @@ impl MinerEngine for GpuEngine {
|
||||
JobContext::new(header_hash, difficulty)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn search_range(&self, ctx: &JobContext, range: Range, cancel: &AtomicBool) -> EngineStatus {
|
||||
if self.contexts.is_empty() {
|
||||
log::warn!("No GPUs available for search.");
|
||||
@@ -276,10 +297,33 @@ impl MinerEngine for GpuEngine {
|
||||
return EngineStatus::Exhausted { hash_count: 0 };
|
||||
}
|
||||
|
||||
// Use first GPU only for now to simplify and avoid threading overhead
|
||||
let gpu_ctx = &self.contexts[0];
|
||||
// Use thread-local assignment for consistent worker-to-GPU mapping
|
||||
let device_index = ASSIGNED_GPU_DEVICE.with(|assigned| {
|
||||
let mut assigned_ref = assigned.borrow_mut();
|
||||
if let Some(index) = *assigned_ref {
|
||||
// This thread already has a GPU assigned
|
||||
index
|
||||
} else {
|
||||
// First time this thread is calling search_range, assign a GPU device
|
||||
let index = if self.contexts.len() == 1 {
|
||||
0
|
||||
} else {
|
||||
self.device_counter.fetch_add(1, Ordering::SeqCst) % self.contexts.len()
|
||||
};
|
||||
*assigned_ref = Some(index);
|
||||
log::info!(
|
||||
"Worker thread assigned to GPU device {} (of {} total devices)",
|
||||
index,
|
||||
self.contexts.len()
|
||||
);
|
||||
index
|
||||
}
|
||||
});
|
||||
|
||||
let gpu_ctx = &self.contexts[device_index];
|
||||
log::debug!(
|
||||
"Starting GPU search on device 0 for nonce range {}..={} (inclusive)",
|
||||
"GPU device {} processing range {}..={} (inclusive)",
|
||||
device_index,
|
||||
range.start,
|
||||
range.end
|
||||
);
|
||||
|
||||
@@ -1276,15 +1276,6 @@ pub async fn run(config: ServiceConfig) -> anyhow::Result<()> {
|
||||
workers = effective_cpus.max(1);
|
||||
}
|
||||
|
||||
// Force workers=1 for GPU engine to prevent concurrent buffer mapping panics
|
||||
if matches!(config.engine, EngineSelection::Gpu) && workers > 1 {
|
||||
log::info!(
|
||||
"GPU engine selected. Forcing workers to 1 (was {}) to avoid buffer contention.",
|
||||
workers
|
||||
);
|
||||
workers = 1;
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Using {workers} worker thread(s) for mining (effective logical CPUs available: {effective_cpus})"
|
||||
);
|
||||
@@ -1338,6 +1329,29 @@ pub async fn run(config: ServiceConfig) -> anyhow::Result<()> {
|
||||
EngineSelection::Gpu => "High-performance GPU mining",
|
||||
}
|
||||
);
|
||||
|
||||
// Adjust worker count for GPU engine based on available devices
|
||||
if matches!(config.engine, EngineSelection::Gpu) {
|
||||
#[cfg(feature = "gpu")]
|
||||
{
|
||||
if let Some(gpu_engine) = engine.as_any().downcast_ref::<engine_gpu::GpuEngine>() {
|
||||
let gpu_device_count = gpu_engine.device_count();
|
||||
if gpu_device_count > 0 {
|
||||
// Use one worker per GPU device, but don't exceed the configured worker count
|
||||
workers = workers.min(gpu_device_count);
|
||||
log::info!(
|
||||
"GPU engine: {} GPU devices detected, using {} workers",
|
||||
gpu_device_count,
|
||||
workers
|
||||
);
|
||||
} else {
|
||||
log::warn!("GPU engine: No GPU devices detected, falling back to 1 worker");
|
||||
workers = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("⚙️ Service configuration: {config}");
|
||||
|
||||
let progress_interval_ms = config.progress_interval_ms.unwrap_or(10000);
|
||||
|
||||
Reference in New Issue
Block a user