From 7d9e04de496915dd9e4544ee18b1a0026dc79242 Mon Sep 17 00:00:00 2001 From: akr Date: Wed, 15 Oct 2014 09:41:25 +0000 Subject: * ext/etc/etc.c (etc_nprocessors_affinity): use sched_getaffinity for getting precious number of available cpus. * ext/etc/etc.c (etc_nprocessors): use etc_nprocessors_affinity if possible. [Feature #10267] etc-nprocessors-kosaki2.patch git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47939 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/etc/etc.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) (limited to 'ext') diff --git a/ext/etc/etc.c b/ext/etc/etc.c index 94b9cc34f0..aaa5025a9c 100644 --- a/ext/etc/etc.c +++ b/ext/etc/etc.c @@ -30,6 +30,10 @@ #include #endif +#ifdef HAVE_SCHED_GETAFFINITY +#include +#endif + static VALUE sPasswd; #ifdef HAVE_GETGRENT static VALUE sGroup; @@ -904,6 +908,54 @@ io_pathconf(VALUE io, VALUE arg) #endif #if (defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)) || defined(_WIN32) + +#ifdef HAVE_SCHED_GETAFFINITY +static int +etc_nprocessors_affin(void) +{ + cpu_set_t *cpuset; + size_t size; + int ret; + int ncpus; + + /* + * XXX: + * man page says CPU_ALLOC takes number of cpus. But it is not accurate + * explanation. sched_getaffinity() returns EINVAL if cpuset bitmap is + * smaller than kernel internal bitmap. + * That said, sched_getaffinity() can fail when a kernel have sparse bitmap + * even if cpuset bitmap is larger than number of cpus. + * The precious way is to use /sys/devices/system/cpu/online. But there are + * two problems, + * - Costly calculation + * It is minor issue, but possibly kill the benefit of parallel processing. + * - No guarantee to exist /sys/devices/system/cpu/online + * This is an issue especially when using containers. + * So, we use hardcode number for workaround. Current linux kernel + * (Linux 3.17) support 8192 cpus at maximum. Then 16384 is enough large. + */ + ncpus = 16384; + + cpuset = CPU_ALLOC(ncpus); + if (!cpuset) { + return -1; + } + size = CPU_ALLOC_SIZE(ncpus); + CPU_ZERO_S(size, cpuset); + + ret = sched_getaffinity(0, size, cpuset); + if (ret==-1) { + goto free; + } + + ret = CPU_COUNT_S(size, cpuset); + free: + CPU_FREE(cpuset); + + return ret; +} +#endif + /* * Returns the number of online processors. * @@ -912,12 +964,21 @@ io_pathconf(VALUE io, VALUE arg) * * This method is implemented as: * - sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux, NetBSD, FreeBSD, OpenBSD, DragonFly BSD, OpenIndiana, Mac OS X, AIX + * - sched_getaffinity(): Linux * * Example: * * require 'etc' * p Etc.nprocessors #=> 4 * + * The result might be smaller number than physical cpus especially when ruby + * process is bound to specific cpus. This is intended for getting better + * parallel processing. + * + * Example: (Linux) + * + * $ taskset 0x3 ./ruby -retc -e "p Etc.nprocessors" #=> 2 + * */ static VALUE etc_nprocessors(VALUE obj) @@ -925,6 +986,17 @@ etc_nprocessors(VALUE obj) long ret; #if !defined(_WIN32) + +#ifdef HAVE_SCHED_GETAFFINITY + int ncpus; + + ncpus = etc_nprocessors_affin(); + if (ncpus != -1) { + return INT2NUM(ncpus); + } + /* fallback to _SC_NPROCESSORS_ONLN */ +#endif + errno = 0; ret = sysconf(_SC_NPROCESSORS_ONLN); if (ret == -1) { -- cgit v1.2.3