simplify
This commit is contained in:
@@ -17,7 +17,6 @@ struct GpuContext {
|
||||
adapter_info: wgpu::AdapterInfo,
|
||||
// Cached vendor configuration
|
||||
optimal_workgroups: u32,
|
||||
estimated_cores: u32,
|
||||
// Reusable buffers
|
||||
header_buffer: wgpu::Buffer,
|
||||
target_buffer: wgpu::Buffer,
|
||||
@@ -192,8 +191,7 @@ impl GpuEngine {
|
||||
log::debug!(target: "gpu_engine", "Buffers and bind group initialized for adapter {}", i);
|
||||
|
||||
// Calculate vendor-specific configuration once during initialization
|
||||
let (optimal_workgroups, estimated_cores) =
|
||||
Self::get_vendor_specific_dispatch(&info, &device);
|
||||
let optimal_workgroups = Self::get_vendor_specific_dispatch(&info, &device);
|
||||
|
||||
contexts.push(Arc::new(GpuContext {
|
||||
device,
|
||||
@@ -202,7 +200,6 @@ impl GpuEngine {
|
||||
bind_group,
|
||||
adapter_info: info,
|
||||
optimal_workgroups,
|
||||
estimated_cores,
|
||||
header_buffer,
|
||||
target_buffer,
|
||||
start_nonce_buffer,
|
||||
@@ -330,10 +327,7 @@ impl MinerEngine for GpuEngine {
|
||||
nonces_per_thread
|
||||
);
|
||||
|
||||
// Track how many nonces this engine has assigned within the requested range so
|
||||
// we don't keep dispatching once we've conceptually covered the whole window.
|
||||
let mut assigned = 0u64;
|
||||
|
||||
// Track how many nonces this engine has processed within the requested range.
|
||||
let mut hash_count = 0u64;
|
||||
|
||||
// Eliminate intermediate syncs - only sync at end or when solution found
|
||||
@@ -351,14 +345,14 @@ impl MinerEngine for GpuEngine {
|
||||
let mut batch_count = 0u32;
|
||||
let total_start = std::time::Instant::now();
|
||||
|
||||
while assigned < total_range_size && current_start <= range.end {
|
||||
while hash_count < total_range_size && current_start <= range.end {
|
||||
if cancel.load(Ordering::Relaxed) {
|
||||
log::debug!(target: "gpu_engine", "GPU 0 cancelled.");
|
||||
return EngineStatus::Cancelled { hash_count };
|
||||
}
|
||||
|
||||
// Determine how much work to do in this batch, capped to the remaining range.
|
||||
let remaining = total_range_size - assigned;
|
||||
let remaining = total_range_size - hash_count;
|
||||
let batch_work = remaining.min(work_per_batch);
|
||||
|
||||
// Update dispatch configuration for this batch:
|
||||
@@ -455,27 +449,23 @@ impl MinerEngine for GpuEngine {
|
||||
// Advance to the next disjoint batch of nonces. We deliberately do not
|
||||
// require contiguity between CPU and GPU engines, only that batches are
|
||||
// non-overlapping within this engine.
|
||||
hash_count += batch_work;
|
||||
assigned = assigned.saturating_add(batch_work);
|
||||
hash_count = hash_count.saturating_add(batch_work);
|
||||
current_start = current_start + U512::from(batch_work);
|
||||
batch_count += 1;
|
||||
}
|
||||
|
||||
let total_time = total_start.elapsed();
|
||||
let effective_hashes = hash_count.min(total_range_size);
|
||||
log::info!(
|
||||
target: "gpu_engine",
|
||||
"GPU finished range. Total time: {:.2}ms, Batches: {}, Hashes: {}, Performance: {:.0} hashes/sec",
|
||||
total_time.as_secs_f64() * 1000.0,
|
||||
batch_count,
|
||||
effective_hashes,
|
||||
effective_hashes as f64 / total_time.as_secs_f64()
|
||||
hash_count,
|
||||
hash_count as f64 / total_time.as_secs_f64()
|
||||
);
|
||||
|
||||
log::debug!(target: "gpu_engine", "GPU 0 finished range.");
|
||||
EngineStatus::Exhausted {
|
||||
hash_count: effective_hashes,
|
||||
}
|
||||
EngineStatus::Exhausted { hash_count }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +474,7 @@ impl GpuEngine {
|
||||
fn get_vendor_specific_dispatch(
|
||||
adapter_info: &wgpu::AdapterInfo,
|
||||
device: &wgpu::Device,
|
||||
) -> (u32, u32) {
|
||||
) -> u32 {
|
||||
let limits = device.limits();
|
||||
let max_workgroups = limits.max_compute_workgroups_per_dimension.min(65535);
|
||||
|
||||
@@ -492,110 +482,90 @@ impl GpuEngine {
|
||||
let vendor_name = adapter_info.name.to_lowercase();
|
||||
let _device_name = adapter_info.device.to_string().to_lowercase();
|
||||
|
||||
// Vendor detection logic without logging (already logged during initialization)
|
||||
// Vendor-specific heuristics based on architecture knowledge
|
||||
let (optimal_workgroups, estimated_cores) = if vendor_name.contains("nvidia")
|
||||
|| adapter_info.vendor == 4318
|
||||
{
|
||||
let optimal_workgroups = if vendor_name.contains("nvidia") || adapter_info.vendor == 4318 {
|
||||
// NVIDIA GPUs (vendor ID 0x10DE = 4318)
|
||||
// CUDA cores benefit from high occupancy, typically thousands of simple cores
|
||||
let workgroups = if vendor_name.contains("rtx 40") || vendor_name.contains("rtx 4090") {
|
||||
(max_workgroups / 8).max(4096) // High-end: RTX 4090 has 16384 CUDA cores
|
||||
(max_workgroups / 8).max(4096)
|
||||
} else if vendor_name.contains("rtx 30") || vendor_name.contains("rtx 20") {
|
||||
(max_workgroups / 12).max(2048) // Mid-high-end: RTX 3080 has ~8700 CUDA cores
|
||||
(max_workgroups / 12).max(2048)
|
||||
} else if vendor_name.contains("gtx") || vendor_name.contains("rtx 16") {
|
||||
(max_workgroups / 16).max(1024) // Mid-range: GTX 1660 has 1408 CUDA cores
|
||||
(max_workgroups / 16).max(1024)
|
||||
} else {
|
||||
(max_workgroups / 20).max(512) // Lower-end NVIDIA
|
||||
(max_workgroups / 20).max(512)
|
||||
};
|
||||
let cores = workgroups * 256 / 2; // NVIDIA benefits from ~2x oversubscription
|
||||
(workgroups, cores)
|
||||
workgroups
|
||||
} else if vendor_name.contains("amd") || adapter_info.vendor == 4098 {
|
||||
// AMD GPUs (vendor ID 0x1002 = 4098)
|
||||
// Compute Units with stream processors, different architecture than NVIDIA
|
||||
let workgroups = if vendor_name.contains("rx 7") || vendor_name.contains("rx 6900") {
|
||||
(max_workgroups / 10).max(3072) // High-end: RX 7900 XTX has 6144 stream processors
|
||||
(max_workgroups / 10).max(3072)
|
||||
} else if vendor_name.contains("rx 6") || vendor_name.contains("rx 5700") {
|
||||
(max_workgroups / 14).max(2048) // Mid-high-end: RX 6800 XT has 4608 stream processors
|
||||
(max_workgroups / 14).max(2048)
|
||||
} else if vendor_name.contains("rx 5") || vendor_name.contains("rx 580") {
|
||||
(max_workgroups / 18).max(1024) // Mid-range: RX 580 has 2304 stream processors
|
||||
(max_workgroups / 18).max(1024)
|
||||
} else {
|
||||
(max_workgroups / 24).max(512) // Lower-end AMD
|
||||
(max_workgroups / 24).max(512)
|
||||
};
|
||||
let cores = workgroups * 256 / 3; // AMD typically benefits from ~3x oversubscription
|
||||
(workgroups, cores)
|
||||
workgroups
|
||||
} else if vendor_name.contains("intel") || adapter_info.vendor == 32902 {
|
||||
// Intel GPUs (vendor ID 0x8086 = 32902)
|
||||
// Newer Xe architecture, different from traditional designs
|
||||
let workgroups = if vendor_name.contains("arc a7") || vendor_name.contains("arc a770") {
|
||||
(max_workgroups / 12).max(2048) // High-end Arc A770
|
||||
(max_workgroups / 12).max(2048)
|
||||
} else if vendor_name.contains("arc a5") || vendor_name.contains("arc a380") {
|
||||
(max_workgroups / 16).max(1024) // Mid-range Arc A380
|
||||
(max_workgroups / 16).max(1024)
|
||||
} else if vendor_name.contains("iris xe") {
|
||||
(max_workgroups / 20).max(512) // Integrated Iris Xe
|
||||
(max_workgroups / 20).max(512)
|
||||
} else {
|
||||
(max_workgroups / 24).max(256) // Other Intel integrated
|
||||
(max_workgroups / 24).max(256)
|
||||
};
|
||||
let cores = workgroups * 256 / 4; // Conservative approach for Intel
|
||||
(workgroups, cores)
|
||||
workgroups
|
||||
} else if adapter_info.backend == wgpu::Backend::Metal {
|
||||
// Apple GPUs (detected by Metal backend)
|
||||
// Apple GPU cores are complex, powerful units - NOT like NVIDIA CUDA cores
|
||||
// Each Apple GPU core can handle many operations in parallel
|
||||
let (gpu_cores, workgroups) = if vendor_name.contains("m4 max") {
|
||||
(40, 800) // M4 Max: 40 GPU cores, ~20x workgroups
|
||||
(40, 800)
|
||||
} else if vendor_name.contains("m4 pro") {
|
||||
(20, 400) // M4 Pro: 20 GPU cores, ~20x workgroups
|
||||
(20, 400)
|
||||
} else if vendor_name.contains("m4") {
|
||||
(10, 200) // M4: 10 GPU cores, ~20x workgroups
|
||||
(10, 200)
|
||||
} else if vendor_name.contains("m3 max") {
|
||||
(40, 800) // M3 Max: 40 GPU cores
|
||||
(40, 800)
|
||||
} else if vendor_name.contains("m3 pro") {
|
||||
(18, 360) // M3 Pro: 18 GPU cores
|
||||
(18, 360)
|
||||
} else if vendor_name.contains("m3") {
|
||||
(10, 200) // M3: 10 GPU cores
|
||||
(10, 200)
|
||||
} else if vendor_name.contains("m2 ultra") {
|
||||
(76, 1520) // M2 Ultra: 76 GPU cores
|
||||
(76, 1520)
|
||||
} else if vendor_name.contains("m2 max") {
|
||||
(38, 760) // M2 Max: 38 GPU cores
|
||||
(38, 760)
|
||||
} else if vendor_name.contains("m2 pro") {
|
||||
(19, 380) // M2 Pro: 19 GPU cores
|
||||
(19, 380)
|
||||
} else if vendor_name.contains("m2") {
|
||||
(10, 200) // M2: 10 GPU cores
|
||||
(10, 200)
|
||||
} else if vendor_name.contains("m1 ultra") {
|
||||
(64, 1280) // M1 Ultra: 64 GPU cores
|
||||
(64, 1280)
|
||||
} else if vendor_name.contains("m1 max") {
|
||||
(32, 640) // M1 Max: 32 GPU cores
|
||||
(32, 640)
|
||||
} else if vendor_name.contains("m1 pro") {
|
||||
(16, 320) // M1 Pro: 16 GPU cores
|
||||
(16, 320)
|
||||
} else if vendor_name.contains("m1") {
|
||||
(8, 160) // M1: 8 GPU cores
|
||||
(8, 160)
|
||||
} else {
|
||||
(8, 160) // Conservative fallback for unknown Apple GPU
|
||||
(8, 160)
|
||||
};
|
||||
|
||||
// Clamp to reasonable limits
|
||||
let clamped_workgroups = workgroups.min(max_workgroups / 4).max(64);
|
||||
|
||||
// Each Apple GPU core is roughly equivalent to 64-128 "effective processing units"
|
||||
// This gives us a realistic estimate of parallel processing capability
|
||||
let estimated_cores = gpu_cores * 96; // Conservative middle ground
|
||||
|
||||
// Apple GPU configuration calculated
|
||||
(clamped_workgroups, estimated_cores)
|
||||
let _ = gpu_cores; // gpu_cores currently unused but kept for potential future tuning
|
||||
clamped_workgroups
|
||||
} else {
|
||||
// Unknown/Generic GPU - use conservative defaults
|
||||
// Unknown/Generic GPU - use conservative defaults (logging moved to initialization)
|
||||
let workgroups = (max_workgroups / 16).max(512);
|
||||
let cores = workgroups * 256 / 4;
|
||||
(workgroups, cores)
|
||||
workgroups
|
||||
};
|
||||
|
||||
log::info!(target: "gpu_engine", "Vendor-specific dispatch configuration:");
|
||||
log::info!(target: "gpu_engine", " Max hardware workgroups: {}", max_workgroups);
|
||||
log::info!(target: "gpu_engine", " Optimal workgroups: {}", optimal_workgroups);
|
||||
log::info!(target: "gpu_engine", " Estimated effective cores: {}", estimated_cores);
|
||||
|
||||
(optimal_workgroups, estimated_cores)
|
||||
optimal_workgroups
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ const MDS_MATRIX_DIAG_12: array<array<u32, 2>, 12> = array<array<u32, 2>, 12>(
|
||||
@group(0) @binding(1) var<storage, read> header: array<u32, 8>; // 32 bytes
|
||||
@group(0) @binding(2) var<storage, read> start_nonce: array<u32, 16>; // 64 bytes
|
||||
@group(0) @binding(3) var<storage, read> difficulty_target: array<u32, 16>; // 64 bytes (U512 target)
|
||||
@group(0) @binding(4) var<storage, read> dispatch_config: array<u32, 4>; // [total_threads, nonces_per_thread, work_per_batch, threads_per_workgroup]
|
||||
@group(0) @binding(4) var<storage, read> dispatch_config: array<u32, 4>; // [total_threads (logical), nonces_per_thread, batch_work (logical nonces in this dispatch), threads_per_workgroup]
|
||||
|
||||
// Goldilocks field element represented as [limb0, limb1, limb2, limb3]
|
||||
// where the value is limb0 + limb1*2^16 + limb2*2^32 + limb3*2^48
|
||||
@@ -938,9 +938,9 @@ fn mining_main(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
||||
|
||||
let thread_id = global_id.x;
|
||||
// Read actual dispatch configuration from buffer
|
||||
let total_threads = dispatch_config[0]; // Logical total threads for this batch
|
||||
let nonces_per_thread = dispatch_config[1]; // Nonces per thread
|
||||
let max_logical_nonces = dispatch_config[2]; // Total logical nonces in this batch (batch_work)
|
||||
let total_threads = dispatch_config[0]; // Total logical threads in this dispatch
|
||||
let nonces_per_thread = dispatch_config[1]; // Nonces processed by each thread
|
||||
let batch_work = dispatch_config[2]; // Number of logical nonces this dispatch is responsible for
|
||||
|
||||
// Guard against threads beyond configured total_threads to avoid overlapping work
|
||||
if (thread_id >= total_threads) {
|
||||
@@ -949,10 +949,10 @@ fn mining_main(@builtin(global_invocation_id) global_id: vec3<u32>) {
|
||||
|
||||
// Work coarsening: each thread processes multiple nonces to reduce batch overhead
|
||||
for (var nonce_offset = 0u; nonce_offset < nonces_per_thread; nonce_offset++) {
|
||||
// Compute logical nonce index within this batch and avoid stepping past batch_work
|
||||
// Compute logical nonce index within this dispatch and avoid stepping past batch_work
|
||||
let logical_index = thread_id + nonce_offset * total_threads;
|
||||
if (logical_index >= max_logical_nonces) {
|
||||
// This thread has exhausted its share in this batch.
|
||||
if (logical_index >= batch_work) {
|
||||
// This thread has exhausted its share of logical nonces for this dispatch.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user