Skip to content

Commit f250c6c

Browse files
authored
unix,win: add uv_available_parallelism() (#3499)
Replacement for the usage pattern where people use uv_cpu_info() as an imperfect heuristic for determining the amount of parallelism that is available to their programs. Fixes #3493.
1 parent c40f8cb commit f250c6c

File tree

5 files changed

+75
-0
lines changed

5 files changed

+75
-0
lines changed

docs/src/misc.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,30 @@ API
334334
335335
.. versionadded:: 1.16.0
336336
337+
.. c:function:: unsigned int uv_available_parallelism(void)
338+
339+
Returns an estimate of the default amount of parallelism a program should
340+
use. Always returns a non-zero value.
341+
342+
On Linux, inspects the calling thread's CPU affinity mask to determine if
343+
it has been pinned to specific CPUs.
344+
345+
On Windows, the available parallelism may be underreported on systems with
346+
more than 64 logical CPUs.
347+
348+
On other platforms, reports the number of CPUs that the operating system
349+
considers to be online.
350+
351+
.. versionadded:: 1.44.0
352+
337353
.. c:function:: int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count)
338354
339355
Gets information about the CPUs on the system. The `cpu_infos` array will
340356
have `count` elements and needs to be freed with :c:func:`uv_free_cpu_info`.
341357
358+
Use :c:func:`uv_available_parallelism` if you need to know how many CPUs
359+
are available for threads or child processes.
360+
342361
.. c:function:: void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count)
343362
344363
Frees the `cpu_infos` array previously allocated with :c:func:`uv_cpu_info`.

include/uv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,7 @@ UV_EXTERN uv_pid_t uv_os_getppid(void);
12421242
UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
12431243
UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority);
12441244

1245+
UV_EXTERN unsigned int uv_available_parallelism(void);
12451246
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
12461247
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
12471248

src/unix/core.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ extern char** environ;
8484
#endif
8585

8686
#if defined(__linux__)
87+
# include <sched.h>
8788
# include <sys/syscall.h>
8889
# define uv__accept4 accept4
8990
#endif
@@ -1648,3 +1649,35 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
16481649
/* Out of tokens (path entries), and no match found */
16491650
return UV_EINVAL;
16501651
}
1652+
1653+
1654+
unsigned int uv_available_parallelism(void) {
1655+
#ifdef __linux__
1656+
cpu_set_t set;
1657+
long rc;
1658+
1659+
memset(&set, 0, sizeof(set));
1660+
1661+
/* sysconf(_SC_NPROCESSORS_ONLN) in musl calls sched_getaffinity() but in
1662+
* glibc it's... complicated... so for consistency try sched_getaffinity()
1663+
* before falling back to sysconf(_SC_NPROCESSORS_ONLN).
1664+
*/
1665+
if (0 == sched_getaffinity(0, sizeof(set), &set))
1666+
rc = CPU_COUNT(&set);
1667+
else
1668+
rc = sysconf(_SC_NPROCESSORS_ONLN);
1669+
1670+
if (rc < 1)
1671+
rc = 1;
1672+
1673+
return (unsigned) rc;
1674+
#else /* __linux__ */
1675+
long rc;
1676+
1677+
rc = sysconf(_SC_NPROCESSORS_ONLN);
1678+
if (rc < 1)
1679+
rc = 1;
1680+
1681+
return (unsigned) rc;
1682+
#endif /* __linux__ */
1683+
}

src/win/util.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,23 @@ int uv_uptime(double* uptime) {
536536
}
537537

538538

539+
unsigned int uv_available_parallelism(void) {
540+
SYSTEM_INFO info;
541+
unsigned rc;
542+
543+
/* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
544+
* with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
545+
*/
546+
GetSystemInfo(&info);
547+
548+
rc = info.dwNumberOfProcessors;
549+
if (rc < 1)
550+
rc = 1;
551+
552+
return rc;
553+
}
554+
555+
539556
int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
540557
uv_cpu_info_t* cpu_infos;
541558
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;

test/test-platform-output.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ TEST_IMPL(platform_output) {
4141
uv_interface_address_t* interfaces;
4242
uv_passwd_t pwd;
4343
uv_utsname_t uname;
44+
unsigned par;
4445
int count;
4546
int i;
4647
int err;
@@ -88,6 +89,10 @@ TEST_IMPL(platform_output) {
8889
printf(" maximum resident set size: %llu\n",
8990
(unsigned long long) rusage.ru_maxrss);
9091

92+
par = uv_available_parallelism();
93+
ASSERT_GE(par, 1);
94+
printf("uv_available_parallelism: %u\n", par);
95+
9196
err = uv_cpu_info(&cpus, &count);
9297
#if defined(__CYGWIN__) || defined(__MSYS__)
9398
ASSERT(err == UV_ENOSYS);

0 commit comments

Comments
 (0)