2020-05-08 14:11:19 +00:00
# include "getNumberOfPhysicalCPUCores.h"
2020-04-16 12:31:57 +00:00
2022-09-28 08:45:15 +00:00
# include "config.h"
2021-12-31 08:39:59 +00:00
# if defined(OS_LINUX)
# include <cmath>
# include <fstream>
# endif
2016-01-13 02:11:40 +00:00
2023-01-06 19:55:55 +00:00
# include <boost/algorithm/string/trim.hpp>
2020-05-08 14:11:19 +00:00
# include <thread>
2023-01-06 19:55:55 +00:00
# include <set>
2020-05-08 14:11:19 +00:00
2021-12-31 08:39:59 +00:00
# if defined(OS_LINUX)
2022-09-10 02:07:51 +00:00
static int32_t readFrom ( const char * filename , int default_value )
2021-12-31 08:39:59 +00:00
{
2022-04-15 20:54:08 +00:00
std : : ifstream infile ( filename ) ;
if ( ! infile . is_open ( ) )
return default_value ;
int idata ;
if ( infile > > idata )
return idata ;
else
return default_value ;
}
2021-12-31 08:39:59 +00:00
2022-04-15 20:54:08 +00:00
/// Try to look at cgroups limit if it is available.
2022-09-10 02:07:51 +00:00
static uint32_t getCGroupLimitedCPUCores ( unsigned default_cpu_count )
2022-04-15 20:54:08 +00:00
{
2022-09-10 02:07:51 +00:00
uint32_t quota_count = default_cpu_count ;
2022-04-15 20:54:08 +00:00
/// Return the number of milliseconds per period process is guaranteed to run.
/// -1 for no quota
int cgroup_quota = readFrom ( " /sys/fs/cgroup/cpu/cpu.cfs_quota_us " , - 1 ) ;
int cgroup_period = readFrom ( " /sys/fs/cgroup/cpu/cpu.cfs_period_us " , - 1 ) ;
2021-12-31 08:39:59 +00:00
if ( cgroup_quota > - 1 & & cgroup_period > 0 )
2022-09-10 02:07:51 +00:00
quota_count = static_cast < uint32_t > ( ceil ( static_cast < float > ( cgroup_quota ) / static_cast < float > ( cgroup_period ) ) ) ;
2021-12-31 08:39:59 +00:00
2022-03-31 15:21:54 +00:00
return std : : min ( default_cpu_count , quota_count ) ;
2021-12-31 08:39:59 +00:00
}
2023-01-22 16:23:35 +00:00
# endif
2023-01-06 19:55:55 +00:00
2023-01-22 16:23:35 +00:00
/// Returns number of physical cores, unlike std::thread::hardware_concurrency() which returns the logical core count. With 2-way SMT
/// (HyperThreading) enabled, physical_concurrency() returns half of of std::thread::hardware_concurrency(), otherwise return the same.
static unsigned physical_concurrency ( )
# if defined(OS_LINUX)
2023-01-06 19:55:55 +00:00
try
{
2023-01-22 16:23:35 +00:00
/// The CPUID instruction isn't reliable across different vendors and CPU models. The best option to get the physical core count is
/// to parse /proc/cpuinfo. boost::thread::physical_concurrency() does the same, so use their implementation.
///
/// See https://doc.callmematthi.eu/static/webArticles/Understanding%20Linux%20_proc_cpuinfo.pdf
2023-01-06 19:55:55 +00:00
std : : ifstream proc_cpuinfo ( " /proc/cpuinfo " ) ;
2023-01-22 16:23:35 +00:00
using CoreEntry = std : : pair < size_t , size_t > ; /// physical id, core id
using CoreEntrySet = std : : set < CoreEntry > ;
CoreEntrySet core_entries ;
2023-01-06 19:55:55 +00:00
CoreEntry cur_core_entry ;
std : : string line ;
while ( std : : getline ( proc_cpuinfo , line ) )
{
size_t pos = line . find ( std : : string ( " : " ) ) ;
if ( pos = = std : : string : : npos )
continue ;
std : : string key = line . substr ( 0 , pos ) ;
std : : string val = line . substr ( pos + 1 ) ;
2023-01-22 16:23:35 +00:00
if ( key . find ( " physical id " ) ! = std : : string : : npos )
2023-01-06 19:55:55 +00:00
{
cur_core_entry . first = std : : stoi ( val ) ;
continue ;
}
2023-01-22 16:23:35 +00:00
if ( key . find ( " core id " ) ! = std : : string : : npos )
2023-01-06 19:55:55 +00:00
{
cur_core_entry . second = std : : stoi ( val ) ;
core_entries . insert ( cur_core_entry ) ;
continue ;
}
}
2023-01-22 16:23:35 +00:00
return core_entries . empty ( ) ? /*unexpected format*/ std : : thread : : hardware_concurrency ( ) : static_cast < unsigned > ( core_entries . size ( ) ) ;
2023-01-06 19:55:55 +00:00
}
catch ( . . . )
{
2023-01-22 16:23:35 +00:00
return std : : thread : : hardware_concurrency ( ) ; /// parsing error
}
# else
{
2023-01-06 19:55:55 +00:00
return std : : thread : : hardware_concurrency ( ) ;
}
2022-04-15 20:54:08 +00:00
# endif
2016-01-13 02:11:40 +00:00
2022-04-15 20:56:51 +00:00
static unsigned getNumberOfPhysicalCPUCoresImpl ( )
2016-01-13 02:11:40 +00:00
{
2023-01-22 16:23:35 +00:00
unsigned cpu_count = std : : thread : : hardware_concurrency ( ) ; /// logical cores (with SMT/HyperThreading)
2023-01-06 19:55:55 +00:00
2023-01-22 16:23:35 +00:00
/// Most x86_64 CPUs have 2-way SMT (Hyper-Threading).
2023-01-06 19:55:55 +00:00
/// Aarch64 and RISC-V don't have SMT so far.
2023-01-22 16:23:35 +00:00
/// POWER has SMT and it can be multi-way (e.g. 8-way), but we don't know how ClickHouse really behaves, so use all of them.
2023-01-06 19:55:55 +00:00
2023-01-23 19:48:09 +00:00
# if defined(__x86_64__) && defined(OS_LINUX)
2023-01-22 16:23:35 +00:00
/// On really big machines, SMT is detrimental to performance (+ ~5% overhead in ClickBench). On such machines, we limit ourself to the physical cores.
/// Few cores indicate it is a small machine, runs in a VM or is a limited cloud instance --> it is reasonable to use all the cores.
if ( cpu_count > = 32 )
cpu_count = physical_concurrency ( ) ;
2023-01-06 19:55:55 +00:00
# endif
2020-07-16 23:12:47 +00:00
2021-12-31 08:39:59 +00:00
# if defined(OS_LINUX)
2022-04-15 20:54:08 +00:00
cpu_count = getCGroupLimitedCPUCores ( cpu_count ) ;
# endif
return cpu_count ;
2016-01-13 02:11:40 +00:00
}
2022-04-15 20:56:51 +00:00
unsigned getNumberOfPhysicalCPUCores ( )
{
/// Calculate once.
static auto res = getNumberOfPhysicalCPUCoresImpl ( ) ;
return res ;
}