From bfdca326801f692439d5f300627cf252aededb7d Mon Sep 17 00:00:00 2001 From: Tomer Levy Date: Fri, 8 Apr 2022 16:34:48 +0300 Subject: [PATCH] Doesn't start the miner if no workers available + added fallback for amd binaries --- .github/workflows/deploy.yaml | 4 +- Cargo.lock | 20 ++++++ plugins/cuda/src/lib.rs | 129 +++++++++++++++++----------------- plugins/opencl/Cargo.toml | 1 + plugins/opencl/src/lib.rs | 67 +++++++++--------- plugins/opencl/src/worker.rs | 81 +++++---------------- src/cli.rs | 8 ++- src/lib.rs | 14 ++-- src/main.rs | 7 +- 9 files changed, 160 insertions(+), 171 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ebcdfe9..ff4443e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -46,8 +46,8 @@ jobs: uses: Jimver/cuda-toolkit@v0.2.5 with: cuda: '11.5.1' - method: 'network' - sub-packages: '["nvcc", "cudart"]' + #method: 'local' + #sub-packages: '["nvcc", "cudart"]' - name: Build on Linux GNU if: matrix.os == 'ubuntu-18.04' diff --git a/Cargo.lock b/Cargo.lock index 959dd7e..edf58b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -648,6 +648,25 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "include_dir" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482a2e29200b7eed25d7fdbd14423326760b7f6658d21a4cf12d55a50713c69f" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e074c19deab2501407c91ba1860fa3d6820bfde307db6d8cb851b55a10be89b" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "1.7.0" @@ -745,6 +764,7 @@ version = "0.1.0" dependencies = [ "clap", "env_logger", + "include_dir", "kaspa-miner", "log", "opencl3", diff --git a/plugins/cuda/src/lib.rs b/plugins/cuda/src/lib.rs index 23e4cea..c7c2d2e 100644 --- a/plugins/cuda/src/lib.rs +++ b/plugins/cuda/src/lib.rs @@ -49,81 +49,82 @@ impl Plugin for CudaPlugin { } //noinspection RsTypeCheck - fn process_option(&mut self, matches: &ArgMatches) -> Result<(), kaspa_miner::Error> { + fn process_option(&mut self, matches: &ArgMatches) -> Result { let opts: CudaOpt = CudaOpt::from_arg_matches(matches)?; self._enabled = !opts.cuda_disable; + if self._enabled { + let gpus: Vec = match &opts.cuda_device { + Some(devices) => devices.clone(), + None => { + let gpu_count = Device::num_devices().unwrap() as u16; + (0..gpu_count).collect() + } + }; + + // if any of cuda_lock_core_clocks / cuda_lock_mem_clocks / cuda_power_limit is valid, init nvml and try to apply + if opts.cuda_lock_core_clocks.is_some() + || opts.cuda_lock_mem_clocks.is_some() + || opts.cuda_power_limits.is_some() + { + for i in 0..gpus.len() { + let lock_mem_clock: Option = match &opts.cuda_lock_mem_clocks { + Some(mem_clocks) if i < mem_clocks.len() => Some(mem_clocks[i]), + Some(mem_clocks) if !mem_clocks.is_empty() => Some(*mem_clocks.last().unwrap()), + _ => None, + }; - let gpus: Vec = match &opts.cuda_device { - Some(devices) => devices.clone(), - None => { - let gpu_count = Device::num_devices().unwrap() as u16; - (0..gpu_count).collect() - } - }; - - // if any of cuda_lock_core_clocks / cuda_lock_mem_clocks / cuda_power_limit is valid, init nvml and try to apply - if opts.cuda_lock_core_clocks.is_some() - || opts.cuda_lock_mem_clocks.is_some() - || opts.cuda_power_limits.is_some() - { - for i in 0..gpus.len() { - let lock_mem_clock: Option = match &opts.cuda_lock_mem_clocks { - Some(mem_clocks) if i < mem_clocks.len() => Some(mem_clocks[i]), - Some(mem_clocks) if !mem_clocks.is_empty() => Some(*mem_clocks.last().unwrap()), - _ => None, - }; - - let lock_core_clock: Option = match &opts.cuda_lock_core_clocks { - Some(core_clocks) if i < core_clocks.len() => Some(core_clocks[i]), - Some(core_clocks) if !core_clocks.is_empty() => Some(*core_clocks.last().unwrap()), - _ => None, - }; - - let power_limit: Option = match &opts.cuda_power_limits { - Some(power_limits) if i < power_limits.len() => Some(power_limits[i]), - Some(power_limits) if !power_limits.is_empty() => Some(*power_limits.last().unwrap()), - _ => None, - }; - - let mut nvml_device: NvmlDevice = self.nvml_instance.device_by_index(gpus[i] as u32)?; - - if let Some(lmc) = lock_mem_clock { - match nvml_device.set_mem_locked_clocks(lmc, lmc) { - Err(e) => error!("set mem locked clocks {:?}", e), - _ => info!("GPU #{} #{} lock mem clock at {} Mhz", i, &nvml_device.name()?, &lmc), + let lock_core_clock: Option = match &opts.cuda_lock_core_clocks { + Some(core_clocks) if i < core_clocks.len() => Some(core_clocks[i]), + Some(core_clocks) if !core_clocks.is_empty() => Some(*core_clocks.last().unwrap()), + _ => None, }; - } - if let Some(lcc) = lock_core_clock { - match nvml_device.set_gpu_locked_clocks(lcc, lcc) { - Err(e) => error!("set gpu locked clocks {:?}", e), - _ => info!("GPU #{} #{} lock core clock at {} Mhz", i, &nvml_device.name()?, &lcc), + let power_limit: Option = match &opts.cuda_power_limits { + Some(power_limits) if i < power_limits.len() => Some(power_limits[i]), + Some(power_limits) if !power_limits.is_empty() => Some(*power_limits.last().unwrap()), + _ => None, }; - }; - if let Some(pl) = power_limit { - match nvml_device.set_power_management_limit(pl * 1000) { - Err(e) => error!("set power limit {:?}", e), - _ => info!("GPU #{} #{} power limit at {} W", i, &nvml_device.name()?, &pl), + let mut nvml_device: NvmlDevice = self.nvml_instance.device_by_index(gpus[i] as u32)?; + + if let Some(lmc) = lock_mem_clock { + match nvml_device.set_mem_locked_clocks(lmc, lmc) { + Err(e) => error!("set mem locked clocks {:?}", e), + _ => info!("GPU #{} #{} lock mem clock at {} Mhz", i, &nvml_device.name()?, &lmc), + }; + } + + if let Some(lcc) = lock_core_clock { + match nvml_device.set_gpu_locked_clocks(lcc, lcc) { + Err(e) => error!("set gpu locked clocks {:?}", e), + _ => info!("GPU #{} #{} lock core clock at {} Mhz", i, &nvml_device.name()?, &lcc), + }; }; - }; + + if let Some(pl) = power_limit { + match nvml_device.set_power_management_limit(pl * 1000) { + Err(e) => error!("set power limit {:?}", e), + _ => info!("GPU #{} #{} power limit at {} W", i, &nvml_device.name()?, &pl), + }; + }; + } } - } - self.specs = (0..gpus.len()) - .map(|i| CudaWorkerSpec { - device_id: gpus[i] as u32, - workload: match &opts.cuda_workload { - Some(workload) if i < workload.len() => workload[i], - Some(workload) if !workload.is_empty() => *workload.last().unwrap(), - _ => DEFAULT_WORKLOAD_SCALE, - }, - is_absolute: opts.cuda_workload_absolute, - blocking_sync: !opts.cuda_no_blocking_sync, - }) - .collect(); - Ok(()) + self.specs = (0..gpus.len()) + .map(|i| CudaWorkerSpec { + device_id: gpus[i] as u32, + workload: match &opts.cuda_workload { + Some(workload) if i < workload.len() => workload[i], + Some(workload) if !workload.is_empty() => *workload.last().unwrap(), + _ => DEFAULT_WORKLOAD_SCALE, + }, + is_absolute: opts.cuda_workload_absolute, + blocking_sync: !opts.cuda_no_blocking_sync, + }) + .collect(); + } + Ok(self.specs.len()) } } diff --git a/plugins/opencl/Cargo.toml b/plugins/opencl/Cargo.toml index 74bbf22..bc1bd5a 100644 --- a/plugins/opencl/Cargo.toml +++ b/plugins/opencl/Cargo.toml @@ -11,6 +11,7 @@ env_logger = "0.9" opencl3 = {version = "0.6", features = ["CL_VERSION_2_1", "CL_VERSION_2_2", "CL_VERSION_3_0"]} log = "0.4" rand = "0.8" +include_dir = "0.7" [lib] crate-type = ["cdylib"] diff --git a/plugins/opencl/src/lib.rs b/plugins/opencl/src/lib.rs index 5396bda..ed94d7c 100644 --- a/plugins/opencl/src/lib.rs +++ b/plugins/opencl/src/lib.rs @@ -45,7 +45,7 @@ impl Plugin for OpenCLPlugin { } //noinspection RsTypeCheck - fn process_option(&mut self, matches: &ArgMatches) -> Result<(), kaspa_miner::Error> { + fn process_option(&mut self, matches: &ArgMatches) -> Result { let opts: OpenCLOpt = OpenCLOpt::from_arg_matches(matches)?; self._enabled = opts.opencl_enable; @@ -77,38 +77,39 @@ impl Plugin for OpenCLPlugin { } None => &platforms[0], }; - info!( - "Chose to mine on {}: {}.", - &_platform.vendor().unwrap_or_else(|_| "Unk".into()), - &_platform.name().unwrap_or_else(|_| "Unk".into()) - ); - - let device_ids = _platform.get_devices(CL_DEVICE_TYPE_ALL).unwrap(); - let gpus = match opts.opencl_device { - Some(dev) => { - self._enabled = true; - dev.iter().map(|d| device_ids[*d as usize]).collect::>() - } - None => device_ids, - }; - - self.specs = (0..gpus.len()) - .map(|i| OpenCLWorkerSpec { - _platform: *_platform, - device_id: Device::new(gpus[i]), - workload: match &opts.opencl_workload { - Some(workload) if i < workload.len() => workload[i], - Some(workload) if !workload.is_empty() => *workload.last().unwrap(), - _ => DEFAULT_WORKLOAD_SCALE, - }, - is_absolute: opts.opencl_workload_absolute, - experimental_amd: opts.experimental_amd, - use_amd_binary: !opts.opencl_no_amd_binary, - random: opts.nonce_gen, - }) - .collect(); - - Ok(()) + if self._enabled { + info!( + "Chose to mine on {}: {}.", + &_platform.vendor().unwrap_or_else(|_| "Unk".into()), + &_platform.name().unwrap_or_else(|_| "Unk".into()) + ); + + let device_ids = _platform.get_devices(CL_DEVICE_TYPE_ALL).unwrap(); + let gpus = match opts.opencl_device { + Some(dev) => { + self._enabled = true; + dev.iter().map(|d| device_ids[*d as usize]).collect::>() + } + None => device_ids, + }; + + self.specs = (0..gpus.len()) + .map(|i| OpenCLWorkerSpec { + _platform: *_platform, + device_id: Device::new(gpus[i]), + workload: match &opts.opencl_workload { + Some(workload) if i < workload.len() => workload[i], + Some(workload) if !workload.is_empty() => *workload.last().unwrap(), + _ => DEFAULT_WORKLOAD_SCALE, + }, + is_absolute: opts.opencl_workload_absolute, + experimental_amd: opts.experimental_amd, + use_amd_binary: !opts.opencl_no_amd_binary, + random: opts.nonce_gen, + }) + .collect(); + } + Ok(self.specs.len()) } } diff --git a/plugins/opencl/src/worker.rs b/plugins/opencl/src/worker.rs index da2a7e9..87a4174 100644 --- a/plugins/opencl/src/worker.rs +++ b/plugins/opencl/src/worker.rs @@ -1,8 +1,9 @@ use crate::cli::NonceGenEnum; use crate::Error; +use include_dir::{include_dir, Dir}; use kaspa_miner::xoshiro256starstar::Xoshiro256StarStar; use kaspa_miner::Worker; -use log::info; +use log::{info, warn}; use opencl3::command_queue::{CommandQueue, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE}; use opencl3::context::Context; use opencl3::device::Device; @@ -18,6 +19,7 @@ use std::ffi::c_void; use std::ptr; use std::sync::Arc; +static BINARY_DIR: Dir = include_dir!("./plugins/opencl/resources/bin/"); static PROGRAM_SOURCE: &str = include_str!("../resources/kaspa-opencl.cl"); pub struct OpenCLGPUWorker { @@ -197,70 +199,21 @@ impl OpenCLGPUWorker { let program = match use_binary { true => { - let device_name = device.name().unwrap_or_else(|_| "Unknown".into()).to_lowercase(); + let mut device_name = device.name().unwrap_or_else(|_| "Unknown".into()).to_lowercase(); + if device_name.contains(':') { + device_name = device_name.split_once(':').expect("We checked for `:`").0.to_string(); + } info!("{}: Looking for binary for {}", name, device_name); - match device_name.as_str() { - /*"ellesmere" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/ellesmere_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|e| { - panic!("{}::Program::create_and_build_from_binary failed: {}", name, String::from(e)) - }),*/ - "gfx906" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx906_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx908" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx908_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx1010" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx1010_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx1011" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx1011_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx1012" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx1012_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx1030" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx1030_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx1031" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx1031_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|_| panic!("{}::Program::create_and_build_from_binary failed", name)), - "gfx1032" => Program::create_and_build_from_binary( - &context, - &[include_bytes!("../resources/bin/gfx1032_kaspa-opencl.bin")], - "", - ) - .unwrap_or_else(|e| panic!("{}::Program::create_and_build_from_binary failed: {}", name, e)), - other => { - panic!( - "{}: Found device {} without prebuilt binary. Trying to run without --opencl-amd-binary.", - name, other - ); + match BINARY_DIR.get_file(format!("{}_kaspa-opencl.bin", device_name)) { + Some(binary) => { + Program::create_and_build_from_binary(&context, &[binary.contents()], "").unwrap_or_else(|e|{ + warn!("{}::Program::create_and_build_from_source failed: {}. Reverting to compiling from source", name, e); + from_source(&context, &device, options).unwrap_or_else(|e| panic!("{}::Program::create_and_build_from_binary failed: {}", name, e)) + }) + }, + None => { + warn!("Binary file not found for {}. Reverting to compiling from source.", device_name); + from_source(&context, &device, options).unwrap_or_else(|e| panic!("{}::Program::create_and_build_from_binary failed: {}", name, e)) } } } diff --git a/src/cli.rs b/src/cli.rs index 2cf1cbb..6a0b47a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -70,8 +70,12 @@ impl Opt { } if !self.kaspad_address.contains("://") { - let port = self.port(); - self.kaspad_address = format!("grpc://{}:{}", self.kaspad_address, port); + let port_str = self.port().to_string(); + let (kaspad, port) = match self.kaspad_address.contains(':') { + true => self.kaspad_address.split_once(':').expect("We checked for `:`"), + false => (self.kaspad_address.as_str(), port_str.as_str()), + }; + self.kaspad_address = format!("grpc://{}:{}", kaspad, port); } log::info!("kaspad address: {}", self.kaspad_address); diff --git a/src/lib.rs b/src/lib.rs index 5b049a8..556c9c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,13 +65,17 @@ impl PluginManager { Ok(specs) } - pub fn process_options(&mut self, matchs: &ArgMatches) -> Result<(), Error> { + /** + Process the options for a plugin, and reports how many workers are available + */ + pub fn process_options(&mut self, matchs: &ArgMatches) -> Result { + let mut count = 0usize; self.plugins.iter_mut().for_each(|plugin| { - plugin + count += plugin .process_option(matchs) .unwrap_or_else(|_| panic!("Could not process option for plugin {}", plugin.name())) }); - Ok(()) + Ok(count) } pub fn has_specs(&self) -> bool { @@ -83,7 +87,7 @@ pub trait Plugin: Any + Send + Sync { fn name(&self) -> &'static str; fn enabled(&self) -> bool; fn get_worker_specs(&self) -> Vec>; - fn process_option(&mut self, matchs: &ArgMatches) -> Result<(), Error>; + fn process_option(&mut self, matchs: &ArgMatches) -> Result; } pub trait WorkerSpec: Any + Send + Sync { @@ -116,7 +120,7 @@ pub fn load_plugins<'help>( for path in paths { app = unsafe { factory.load_single_plugin(app, path.as_str()).unwrap_or_else(|(app, e)| { - eprintln!("Failed loading plugin {}: {}", path, e); + eprintln!("WARNING: Failed loading plugin {} (ignore if you do not intend to use): {}", path, e); app }) }; diff --git a/src/main.rs b/src/main.rs index aec3195..8e40553 100644 --- a/src/main.rs +++ b/src/main.rs @@ -117,11 +117,16 @@ async fn main() -> Result<(), Error> { let matches = app.get_matches(); - plugin_manager.process_options(&matches)?; + let worker_count = plugin_manager.process_options(&matches)?; let mut opt: Opt = Opt::from_arg_matches(&matches)?; opt.process()?; env_logger::builder().filter_level(opt.log_level()).parse_default_env().init(); info!("Found plugins: {:?}", plugins); + info!("Plugins found {} workers", worker_count); + if worker_count == 0 && opt.num_threads.unwrap_or(0) == 0 { + error!("No workers specified"); + return Err("No workers specified".into()); + } let block_template_ctr = Arc::new(AtomicU16::new((thread_rng().next_u64() % 10_000u64) as u16)); if opt.devfund_percent > 0 {