Allow overriding of std:🧵:hardware concurrency
(#3948)
* Add `get_env_int_or_default` * Add `nano::hardware_concurrency` * Use `nano::hardware_concurrency` when checking available cores * Print info about logical cores at startup * Lexical casts may throw exceptions if the string is invalid. Since this is explicitly overriden by the user, and called in several non-exception safe contexts, log to cerr the reason why the exception was thrown. * Comment Co-authored-by: clemahieu <clemahieu@gmail.com>
This commit is contained in:
parent
2893046c5e
commit
684fb3a8bb
12 changed files with 87 additions and 18 deletions
|
@ -2,6 +2,7 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <valgrind/valgrind.h>
|
||||
|
@ -229,12 +230,6 @@ uint8_t get_pre_release_node_version ()
|
|||
return boost::numeric_cast<uint8_t> (boost::lexical_cast<int> (NANO_PRE_RELEASE_VERSION_STRING));
|
||||
}
|
||||
|
||||
std::string get_env_or_default (char const * variable_name, std::string default_value)
|
||||
{
|
||||
auto value = getenv (variable_name);
|
||||
return value ? value : default_value;
|
||||
}
|
||||
|
||||
uint64_t get_env_threshold_or_default (char const * variable_name, uint64_t const default_value)
|
||||
{
|
||||
auto * value = getenv (variable_name);
|
||||
|
@ -321,8 +316,43 @@ std::string get_tls_toml_config_path (boost::filesystem::path const & data_path)
|
|||
}
|
||||
} // namespace nano
|
||||
|
||||
std::optional<std::string> nano::get_env (const char * variable_name)
|
||||
{
|
||||
auto value = std::getenv (variable_name);
|
||||
if (value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string nano::get_env_or_default (char const * variable_name, std::string default_value)
|
||||
{
|
||||
auto value = nano::get_env (variable_name);
|
||||
return value ? *value : default_value;
|
||||
}
|
||||
|
||||
int nano::get_env_int_or_default (const char * variable_name, const int default_value)
|
||||
{
|
||||
auto value = nano::get_env (variable_name);
|
||||
if (value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return boost::lexical_cast<int> (*value);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// It is unexpected that this exception will be caught, log to cerr the reason.
|
||||
std::cerr << boost::str (boost::format ("Error parsing environment variable: %1% value: %2%") % variable_name % *value);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
uint32_t nano::test_scan_wallet_reps_delay ()
|
||||
{
|
||||
auto test_env = nano::get_env_or_default ("NANO_TEST_WALLET_SCAN_REPS_DELAY", "900000"); // 15 minutes by default
|
||||
return boost::lexical_cast<uint32_t> (test_env);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace boost
|
||||
|
@ -87,7 +88,22 @@ uint8_t get_minor_node_version ();
|
|||
uint8_t get_patch_node_version ();
|
||||
uint8_t get_pre_release_node_version ();
|
||||
|
||||
/*
|
||||
* Environment variables
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get environment variable as string or none if variable is not present
|
||||
*/
|
||||
std::optional<std::string> get_env (char const * variable_name);
|
||||
/*
|
||||
* Get environment variable as string or `default_value` if variable is not present
|
||||
*/
|
||||
std::string get_env_or_default (char const * variable_name, std::string const default_value);
|
||||
/*
|
||||
* Get environment variable as int or `default_value` if variable is not present
|
||||
*/
|
||||
int get_env_int_or_default (char const * variable_name, int const default_value);
|
||||
uint64_t get_env_threshold_or_default (char const * variable_name, uint64_t const default_value);
|
||||
|
||||
uint16_t test_node_port ();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
|
@ -24,6 +25,6 @@ public:
|
|||
|
||||
bool enable{ false };
|
||||
uint8_t memory_multiplier{ 2 };
|
||||
unsigned io_threads{ std::thread::hardware_concurrency () };
|
||||
unsigned io_threads{ nano::hardware_concurrency () };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/threading.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
@ -53,7 +55,7 @@ class rpc_process_config final
|
|||
public:
|
||||
rpc_process_config (nano::network_constants & network_constants);
|
||||
nano::network_constants & network_constants;
|
||||
unsigned io_threads{ (4 < std::thread::hardware_concurrency ()) ? std::thread::hardware_concurrency () : 4 };
|
||||
unsigned io_threads{ std::max (nano::hardware_concurrency (), 4u) };
|
||||
std::string ipc_address;
|
||||
uint16_t ipc_port{ network_constants.default_ipc_port };
|
||||
unsigned num_ipc_connections{ (network_constants.is_live_network () || network_constants.is_test_network ()) ? 8u : network_constants.is_beta_network () ? 4u
|
||||
|
|
|
@ -308,3 +308,15 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (th
|
|||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "count", thread_pool.num_queued_tasks (), sizeof (std::function<void ()>) }));
|
||||
return composite;
|
||||
}
|
||||
|
||||
unsigned int nano::hardware_concurrency ()
|
||||
{
|
||||
// Try to read overridden value from environment variable
|
||||
static int value = nano::get_env_int_or_default ("NANO_HARDWARE_CONCURRENCY", 0);
|
||||
if (value <= 0)
|
||||
{
|
||||
// Not present or invalid, use default
|
||||
return std::thread::hardware_concurrency ();
|
||||
}
|
||||
return value;
|
||||
}
|
|
@ -201,4 +201,9 @@ private:
|
|||
};
|
||||
|
||||
std::unique_ptr<nano::container_info_component> collect_container_info (thread_pool & thread_pool, std::string const & name);
|
||||
|
||||
/*
|
||||
* Number of available logical processor cores. Might be overridden by setting `NANO_HARDWARE_CONCURRENCY` environment variable
|
||||
*/
|
||||
unsigned int hardware_concurrency ();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ nano::work_pool::work_pool (nano::network_constants & network_constants, unsigne
|
|||
static_assert (ATOMIC_INT_LOCK_FREE == 2, "Atomic int needed");
|
||||
boost::thread::attributes attrs;
|
||||
nano::thread_attributes::set (attrs);
|
||||
auto count (network_constants.is_dev_network () ? std::min (max_threads_a, 1u) : std::min (max_threads_a, std::max (1u, boost::thread::hardware_concurrency ())));
|
||||
auto count (network_constants.is_dev_network () ? std::min (max_threads_a, 1u) : std::min (max_threads_a, std::max (1u, nano::hardware_concurrency ())));
|
||||
if (opencl)
|
||||
{
|
||||
// One thread to handle OpenCL
|
||||
|
|
|
@ -117,6 +117,9 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
|
|||
std::cout << initialization_text << std::endl;
|
||||
logger.always_log (initialization_text);
|
||||
|
||||
// Print info about number of logical cores detected, those are used to decide how many IO, worker and signature checker threads to spawn
|
||||
logger.always_log (boost::format ("Hardware concurrency: %1% ( configured: %2% )") % std::thread::hardware_concurrency () % nano::hardware_concurrency ());
|
||||
|
||||
nano::set_file_descriptor_limit (OPEN_FILE_DESCRIPTORS_LIMIT);
|
||||
auto const file_descriptor_limit = nano::get_file_descriptor_limit ();
|
||||
if (file_descriptor_limit < OPEN_FILE_DESCRIPTORS_LIMIT)
|
||||
|
|
|
@ -56,16 +56,16 @@ public:
|
|||
nano::amount online_weight_minimum{ 60000 * nano::Gxrb_ratio };
|
||||
unsigned election_hint_weight_percent{ 50 };
|
||||
unsigned password_fanout{ 1024 };
|
||||
unsigned io_threads{ std::max<unsigned> (4, std::thread::hardware_concurrency ()) };
|
||||
unsigned network_threads{ std::max<unsigned> (4, std::thread::hardware_concurrency ()) };
|
||||
unsigned work_threads{ std::max<unsigned> (4, std::thread::hardware_concurrency ()) };
|
||||
unsigned io_threads{ std::max (4u, nano::hardware_concurrency ()) };
|
||||
unsigned network_threads{ std::max (4u, nano::hardware_concurrency ()) };
|
||||
unsigned work_threads{ std::max (4u, nano::hardware_concurrency ()) };
|
||||
/* Use half available threads on the system for signature checking. The calling thread does checks as well, so these are extra worker threads */
|
||||
unsigned signature_checker_threads{ std::thread::hardware_concurrency () / 2 };
|
||||
unsigned signature_checker_threads{ std::max (2u, nano::hardware_concurrency () / 2) };
|
||||
bool enable_voting{ false };
|
||||
unsigned bootstrap_connections{ 4 };
|
||||
unsigned bootstrap_connections_max{ 64 };
|
||||
unsigned bootstrap_initiator_threads{ 1 };
|
||||
unsigned bootstrap_serving_threads{ std::max<unsigned> (2, std::thread::hardware_concurrency () / 2) };
|
||||
unsigned bootstrap_serving_threads{ std::max (2u, nano::hardware_concurrency () / 2) };
|
||||
uint32_t bootstrap_frontier_request_count{ 1024 * 1024 };
|
||||
nano::websocket::config websocket_config;
|
||||
nano::diagnostics_config diagnostics_config;
|
||||
|
|
|
@ -744,7 +744,7 @@ bool nano::rocksdb::store::copy_db (boost::filesystem::path const & destination_
|
|||
backup_options.share_table_files = true;
|
||||
|
||||
// Increase number of threads used for copying
|
||||
backup_options.max_background_operations = std::thread::hardware_concurrency ();
|
||||
backup_options.max_background_operations = nano::hardware_concurrency ();
|
||||
auto status = ::rocksdb::BackupEngine::Open (::rocksdb::Env::Default (), backup_options, &backup_engine_raw);
|
||||
backup_engine.reset (backup_engine_raw);
|
||||
if (!status.ok ())
|
||||
|
|
|
@ -9,7 +9,7 @@ template <typename T>
|
|||
void parallel_traversal (std::function<void (T const &, T const &, bool const)> const & action)
|
||||
{
|
||||
// Between 10 and 40 threads, scales well even in low power systems as long as actions are I/O bound
|
||||
unsigned const thread_count = std::max (10u, std::min (40u, 10 * std::thread::hardware_concurrency ()));
|
||||
unsigned const thread_count = std::max (10u, std::min (40u, 10 * nano::hardware_concurrency ()));
|
||||
T const value_max{ std::numeric_limits<T>::max () };
|
||||
T const split = value_max / thread_count;
|
||||
std::vector<std::thread> threads;
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace test
|
|||
boost::asio::io_context io_ctx;
|
||||
std::vector<std::shared_ptr<nano::node>> nodes;
|
||||
nano::logging logging;
|
||||
nano::work_pool work{ nano::dev::network_params.network, std::max (std::thread::hardware_concurrency (), 1u) };
|
||||
nano::work_pool work{ nano::dev::network_params.network, std::max (nano::hardware_concurrency (), 1u) };
|
||||
std::chrono::time_point<std::chrono::steady_clock, std::chrono::duration<double>> deadline{ std::chrono::steady_clock::time_point::max () };
|
||||
double deadline_scaling_factor{ 1.0 };
|
||||
unsigned node_sequence{ 0 };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue