From 09df7810d1dbaad2411a42a9af4584c8b4109371 Mon Sep 17 00:00:00 2001 From: Ganesh Kathiresan Date: Wed, 26 Feb 2020 20:18:50 +0530 Subject: [PATCH] Merge pull request #16457 from ganesh-k13:bugfix/getCPUCount-fix * Fixed getCPUCount Minor new line changes Android fix | efficient linux checks Android fix 2 Fixed cpu logic for non linux platforms Android fix 3 Android fix 4 * No v1 case handle | Refactor long lines * Refined Cgroups logic | Combine Android and Linux * Fixed directives * Added support for --cpus | Fixed minor bug in Andriod | Change file read logic * Added macro checks for apple errors * Fixed macro to include android * Addressed review comments * Fixed android macro * Refined return values * Fixed apple warning * Addressed review comments * Fixed whitespace * Android Fix try 1 * Android Fix try 2 * Android Fix try 3 * Removed unwanted endif * Android Fix try 4 * Android Fix try 5 * Macro Restructure * core: updates to CPUs detection (minor) --- modules/core/src/parallel.cpp | 142 +++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 20 deletions(-) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 9dd8648a72..0ac4c4aca3 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -58,13 +58,20 @@ #include #include #include + #include #if defined __ANDROID__ #include + #include + #include #elif defined __APPLE__ #include #endif #endif +#if defined CV_CXX11 + #include +#endif + #ifdef _OPENMP #define HAVE_OPENMP #endif @@ -739,19 +746,40 @@ int cv::getThreadNum(void) #endif } -#ifdef __ANDROID__ -static inline int getNumberOfCPUsImpl() + +#if defined __linux__ || defined __GLIBC__ || defined __EMSCRIPTEN__ || defined __HAIKU__ || defined __ANDROID__ + #define CV_CPU_GROUPS_1 +#endif + +#if defined __linux__ || defined __ANDROID__ + #define CV_HAVE_CGROUPS 1 +#endif + +#if defined CV_CPU_GROUPS_1 +static inline +std::string getFileContents(const char *filename) { - FILE* cpuPossible = fopen("/sys/devices/system/cpu/possible", "r"); - if(!cpuPossible) - return 1; + std::ifstream ifs(filename); + if (!ifs.is_open()) + return std::string(); + + std::string content( (std::istreambuf_iterator(ifs) ), + (std::istreambuf_iterator() ) ); - char buf[2000]; //big enough for 1000 CPUs in worst possible configuration - char* pbuf = fgets(buf, sizeof(buf), cpuPossible); - fclose(cpuPossible); - if(!pbuf) - return 1; + if (ifs.fail()) + return std::string(); + return content; +} + +static inline +int getNumberOfCPUsImpl(const char *filename) +{ + std::string file_contents = getFileContents(filename); + if(file_contents.empty()) + return 0; + + char *pbuf = const_cast(file_contents.c_str()); //parse string of form "0-1,3,5-7,10,13-15" int cpusAvailable = 0; @@ -775,12 +803,63 @@ static inline int getNumberOfCPUsImpl() } } - return cpusAvailable ? cpusAvailable : 1; + return cpusAvailable; } #endif +#if defined CV_HAVE_CGROUPS +static inline +unsigned getNumberOfCPUsCFS() +{ + int cfs_quota = 0; + { + std::ifstream ss_period("/sys/fs/cgroup/cpu/cpu.cfs_quota_us", std::ios::in | std::ios::binary); + ss_period >> cfs_quota; + + if (ss_period.fail() || cfs_quota < 1) /* cfs_quota must not be 0 or negative */ + return 0; + } + + int cfs_period = 0; + { + std::ifstream ss_quota("/sys/fs/cgroup/cpu/cpu.cfs_period_us", std::ios::in | std::ios::binary); + ss_quota >> cfs_period; + + if (ss_quota.fail() || cfs_period < 1) + return 0; + } + + return (unsigned)max(1, cfs_quota/cfs_period); +} +#endif + +template static inline +T minNonZero(const T& val_1, const T& val_2) +{ + if ((val_1 != 0) && (val_2 != 0)) + return std::min(val_1, val_2); + return (val_1 != 0) ? val_1 : val_2; +} + int cv::getNumberOfCPUs(void) { + /* + * Logic here is to try different methods of getting CPU counts and return + * the minimum most value as it has high probablity of being right and safe. + * Return 1 if we get 0 or not found on all methods. + */ +#if defined CV_CXX11 + /* + * Check for this standard C++11 way, we do not return directly because + * running in a docker or K8s environment will mean this is the host + * machines config not the containers or pods and as per docs this value + * must be "considered only a hint". + */ + unsigned ncpus = std::thread::hardware_concurrency(); /* If the value is not well defined or not computable, returns 0 */ +#else + unsigned ncpus = 0; /* 0 means we have to find out some other way */ +#endif + #if defined _WIN32 SYSTEM_INFO sysinfo; #if (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_X64) || defined(WINRT)) && _WIN32_WINNT >= 0x501 @@ -788,13 +867,37 @@ int cv::getNumberOfCPUs(void) #else GetSystemInfo( &sysinfo ); #endif + unsigned ncpus_sysinfo = sysinfo.dwNumberOfProcessors < 0 ? 1 : sysinfo.dwNumberOfProcessors; /* Just a fail safe */ + ncpus = minNonZero(ncpus, ncpus_sysinfo); + +#elif defined CV_CPU_GROUPS_1 + +#if defined CV_HAVE_CGROUPS + static unsigned ncpus_impl_cpuset = (unsigned)getNumberOfCPUsImpl("/sys/fs/cgroup/cpuset/cpuset.cpus"); + ncpus = minNonZero(ncpus, ncpus_impl_cpuset); + + static unsigned ncpus_impl_cfs = getNumberOfCPUsCFS(); + ncpus = minNonZero(ncpus, ncpus_impl_cfs); +#endif + + static unsigned ncpus_impl_devices = (unsigned)getNumberOfCPUsImpl("/sys/devices/system/cpu/online"); + ncpus = minNonZero(ncpus, ncpus_impl_devices); + +#if defined _GNU_SOURCE \ + && !defined(__ANDROID__) // TODO: add check for modern Android NDK + + cpu_set_t cpu_set; + if (0 == sched_getaffinity(0, sizeof(cpu_set), &cpu_set)) + { + unsigned cpu_count_cpu_set = CPU_COUNT(&cpu_set); + ncpus = minNonZero(ncpus, cpu_count_cpu_set); + } + +#endif + + static unsigned cpu_count_sysconf = (unsigned)sysconf( _SC_NPROCESSORS_ONLN ); + ncpus = minNonZero(ncpus, cpu_count_sysconf); - return (int)sysinfo.dwNumberOfProcessors; -#elif defined __ANDROID__ - static int ncpus = getNumberOfCPUsImpl(); - return ncpus; -#elif defined __linux__ || defined __GLIBC__ || defined __HAIKU__ || defined __EMSCRIPTEN__ - return (int)sysconf( _SC_NPROCESSORS_ONLN ); #elif defined __APPLE__ int numCPU=0; int mib[4]; @@ -816,10 +919,9 @@ int cv::getNumberOfCPUs(void) numCPU = 1; } - return (int)numCPU; -#else - return 1; + ncpus = minNonZero(ncpus, (unsigned)numCPU); #endif + return ncpus != 0 ? ncpus : 1; } const char* cv::currentParallelFramework() {