-
Notifications
You must be signed in to change notification settings - Fork 18k
runtime: NumCPU() should respect zone CPU cap on illumos #35199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Labels
Comments
I wrote a short C program that demonstrates roughly what we'll need to do to check for the cap: #include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <rctl.h>
#include <err.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
int32_t sysconf_ncpus = (int32_t)sysconf(_SC_NPROCESSORS_ONLN);
printf("ncpu = %d\n", sysconf_ncpus);
rctlblk_t *rblks[] = {
calloc(1, rctlblk_size()),
calloc(1, rctlblk_size()),
};
/*
* As per resource_controls(5), the "zone.cpu-cap" resource control is
* "the percentage of a single CPU that can be used by all user threads
* in a zone, expressed as an integer." That is, a value of 100 means
* one whole CPU on this system. If there is no cap, or the cap is
* greater than the number of actual detected CPUs, we just use the CPU
* count value.
*/
uint64_t capval = 0;
int flag = RCTL_FIRST;
for (unsigned i = 0; ; i++) {
int flag = i == 0 ? RCTL_FIRST : RCTL_NEXT;
rctlblk_t *rblk = rblks[i % 2];
rctlblk_t *rblkprev = rblks[(i + 1) % 2];
if (getrctl("zone.cpu-cap", rblkprev, rblk, flag) != 0) {
if (errno == ENOENT) {
break;
}
err(1, "getrctl");
}
int s = 0;
if (!(rctlblk_get_local_flags(rblk) & RCTL_LOCAL_MAXIMAL) &&
rctlblk_get_local_action(rblk, &s) == RCTL_LOCAL_DENY) {
rctl_qty_t v = rctlblk_get_value(rblk);
if (capval == 0 || capval > v) {
capval = v;
}
}
}
if (capval == 0) {
printf("uncapped, so %d CPUs\n", sysconf_ncpus);
} else {
int cap_ncpus = capval / 100;
if (cap_ncpus > sysconf_ncpus) {
printf("cap too large, so %d CPUs\n", sysconf_ncpus);
} else {
printf("capped, at %d CPUs\n", cap_ncpus);
}
}
} Run on a system with 8 CPUs and no cap configured:
Run on the current illumos buildlet host:
|
Change https://golang.org/cl/203758 mentions this issue: |
I tested this on a couple of systems with a simple program: package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Printf("runtime.NumCPU() = %v\n", runtime.NumCPU())
} With no cap and 8 CPUs:
With a cap and 48 CPUs:
|
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
In the machine independent
runtime
bits:On illumos, this is initialised using sysconf(3C) with the
_SC_NPROCESSORS_ONLN
variable, returning the "number of processors online":The operating system affords several different mechanisms for controlling (capping) the resources used by a zone. One is to place the zone in a processor set, which would generally exclude other zones from sharing the CPUs in the set. When in configured to run under a processor set, the
_SC_NPROCESSORS_ONLN
value would reflect the number of processors in that set, which is fine.Another common limiting mechanism is a CPU cap, which works differently. All available processors are still visible as online, and are potentially available for use. The cap is enforced by preventing processes from running for more than the configured quantity of CPU seconds per second. For example, if a system has 48 processors and the zone has a cap equivalent to 2 CPUs, the zone may expend its cap running just two threads on two actual CPUs, or on 48 threads each running for 1/24 seconds per wall clock second.
It should be reasonably easy to enhance the
getncpu()
function to check for a CPU cap when determining the count of usable CPUs.The text was updated successfully, but these errors were encountered: