ClickHouse/src/Common/getNumberOfPhysicalCPUCores.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

124 lines
4.0 KiB
C++
Raw Normal View History

#include "getNumberOfPhysicalCPUCores.h"
#include "config.h"
#if defined(OS_LINUX)
# include <cmath>
# include <fstream>
#endif
2023-01-06 19:55:55 +00:00
#include <boost/algorithm/string/trim.hpp>
#include <thread>
2023-01-06 19:55:55 +00:00
#include <set>
#if defined(OS_LINUX)
2022-09-10 02:07:51 +00:00
static int32_t readFrom(const char * filename, int default_value)
{
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;
}
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);
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)));
return std::min(default_cpu_count, quota_count);
}
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
2022-04-15 20:56:51 +00:00
static unsigned getNumberOfPhysicalCPUCoresImpl()
{
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
#if defined(OS_LINUX)
2022-04-15 20:54:08 +00:00
cpu_count = getCGroupLimitedCPUCores(cpu_count);
#endif
return cpu_count;
}
2022-04-15 20:56:51 +00:00
unsigned getNumberOfPhysicalCPUCores()
{
/// Calculate once.
static auto res = getNumberOfPhysicalCPUCoresImpl();
return res;
}