From 2cc49b3877258662acd333dbc67418d118e8bf9e Mon Sep 17 00:00:00 2001 From: Petr Date: Sat, 1 Jul 2023 21:34:08 +0200 Subject: [PATCH] Run miner threads at priority `SCHED_IDLE` (see `sched(7)`) This way miners can fully utilize all spare CPU power without affecting performance of the rest of the system. --- src/cryptonote_basic/miner.cpp | 24 +++++++- src/cryptonote_basic/miner.h | 3 +- src/cryptonote_basic/thread.h | 100 +++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/cryptonote_basic/thread.h diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 91ee86d601..49b440e16a 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -83,6 +83,7 @@ using namespace epee; #include "miner.h" #include "crypto/hash.h" +#include "thread.h" extern "C" void slow_hash_allocate_state(); @@ -95,6 +96,7 @@ namespace cryptonote const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + const command_line::arg_descriptor arg_mining_threads_idle = {"mining-threads-idle", "Run mining threads at the idle/lowest possible system priority. This is an alternative to the background mining feature.", false, true}; const command_line::arg_descriptor arg_bg_mining_enable = {"bg-mining-enable", "enable background mining", true, true}; const command_line::arg_descriptor arg_bg_mining_ignore_battery = {"bg-mining-ignore-battery", "if true, assumes plugged in when unable to query system power status", false, true}; const command_line::arg_descriptor arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true}; @@ -120,6 +122,7 @@ namespace cryptonote m_total_hashes(0), m_do_print_hashrate(false), m_do_mining(false), + m_threads_idle(false), m_current_hash_rate(0), m_is_background_mining_enabled(false), m_min_idle_seconds(BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS), @@ -287,6 +290,7 @@ namespace cryptonote command_line::add_arg(desc, arg_extra_messages); command_line::add_arg(desc, arg_start_mining); command_line::add_arg(desc, arg_mining_threads); + command_line::add_arg(desc, arg_mining_threads_idle); command_line::add_arg(desc, arg_bg_mining_enable); command_line::add_arg(desc, arg_bg_mining_ignore_battery); command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds); @@ -350,6 +354,11 @@ namespace cryptonote if(command_line::has_arg(vm, arg_bg_mining_miner_target_percentage)) set_mining_target( command_line::get_arg(vm, arg_bg_mining_miner_target_percentage) ); + if(command_line::has_arg(vm, arg_mining_threads_idle)) + { + m_threads_idle = command_line::get_arg(vm, arg_mining_threads_idle); + } + return true; } //----------------------------------------------------------------------------------------------------- @@ -367,7 +376,7 @@ namespace cryptonote return m_threads_total; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, bool do_background, bool ignore_battery) + bool miner::start(const account_public_address& adr, size_t threads_count, bool threads_idle, bool do_background, bool ignore_battery) { m_block_reward = 0; m_mine_address = adr; @@ -401,7 +410,13 @@ namespace cryptonote for(size_t i = 0; i != m_threads_total; i++) { - m_threads.push_back(boost::thread(m_attrs, boost::bind(&miner::worker_thread, this))); + if (threads_idle) { + m_threads.push_back(create_background_thread(m_attrs, boost::bind( + &miner::worker_thread, this))); + } else { + m_threads.push_back(boost::thread(m_attrs, boost::bind( + &miner::worker_thread, this))); + } } if (threads_count == 0) @@ -411,6 +426,9 @@ namespace cryptonote if( get_is_background_mining_enabled() ) { + if (threads_idle) { + MERROR("Feature --mining-threads-idle is an alternative to background mining and doesn't make sense to be used together."); + } m_background_mining_thread = boost::thread(m_attrs, boost::bind(&miner::background_worker_thread, this)); LOG_PRINT_L0("Background mining controller thread started" ); } @@ -493,7 +511,7 @@ namespace cryptonote { if(m_do_mining) { - start(m_mine_address, m_threads_total, get_is_background_mining_enabled(), get_ignore_battery()); + start(m_mine_address, m_threads_total, m_threads_idle, get_is_background_mining_enabled(), get_ignore_battery()); } } //----------------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 35b988bd77..2fec92ee36 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -66,7 +66,7 @@ namespace cryptonote static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height, uint64_t block_reward); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, bool do_background = false, bool ignore_battery = false); + bool start(const account_public_address& adr, size_t threads_count, bool threads_idle, bool do_background = false, bool ignore_battery = false); uint64_t get_speed() const; uint32_t get_threads_count() const; void send_stop_signal(); @@ -150,6 +150,7 @@ namespace cryptonote std::list m_last_hash_rates; bool m_do_print_hashrate; bool m_do_mining; + bool m_threads_idle; std::vector> m_threads_autodetect; boost::thread::attributes m_attrs; diff --git a/src/cryptonote_basic/thread.h b/src/cryptonote_basic/thread.h new file mode 100644 index 0000000000..88c72332e0 --- /dev/null +++ b/src/cryptonote_basic/thread.h @@ -0,0 +1,100 @@ +// Copyright (c) 2014-2022, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include + +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif // _WIN32 + +namespace cryptonote +{ + +// Creates and returns a thread running at an idle priority (if possible). +template +boost::thread create_background_thread(boost::thread::attributes& attrs, Callable func); + +#ifdef _WIN32 + +template +boost::thread create_background_thread(boost::thread::attributes& attrs, Callable func) { + return boost::thread(attrs, [func = std::move(func)]() mutable { + auto handle = GetCurrentThread(); + if (!SetThreadPriority(handle, THREAD_MODE_BACKGROUND_BEGIN)) { + if (!SetThreadPriority(handle, THREAD_PRIORITY_LOWEST)) { + MWARNING("Can't set a background thread priority: " + << std::error_code(GetLastError(), std::system_category())); + + } + } + std::move(func)(); + }); +} + +#else + +template +boost::thread create_background_thread(boost::thread::attributes& attrs, Callable func) { + auto thread = boost::thread(attrs, std::move(func)); + auto handle = thread.native_handle(); + int policy; + struct sched_param param; + int error; + if ((error = pthread_getschedparam(handle, &policy, ¶m)) != 0) { + MWARNING("Can't set a background thread priority: pthread_getschedparam " + "failed: " << std::strerror(error)); + return thread; + } +#ifdef SCHED_IDLE + policy = SCHED_IDLE; + // In particular MacOS doesn't support `SCHED_IDLE`. In the future we might + // consider using https://developer.apple.com/documentation/dispatch + // instead. +#else +#warning SCHED_IDLE policy not available, falling back to minimum priority + param.sched_priority = sched_get_priority_min(policy); +#endif + if ((error = pthread_setschedparam(handle, policy, ¶m)) != 0) { + MWARNING("Can't set a background thread priority: pthread_setschedparam " + "to SCHED_IDLE failed: " << std::strerror(error)); + } + return thread; +} + +#endif + +} // namespace cryptonote