Hello, everyone.

Not sure if this is the appropriate channel to ask, or
whether I should just contact the libgomp maintainers
directly.

GCC interests me a lot and I have been submitting
patches for some nits, but not code that would solve
a PR yet.

I noticed on PR115367 and some adjacent links [1] that
the libgomp implementation of OMP_DYNAMIC is not exactly
popular, and it is noted that the library code itself
remarks a lack of guideline/recommendation for how many
threads it should actually spawn.

The current approach for Linux is, whenever a parallel
block starts, we call glibc getloadavg, take the 15min
average, and decide to use "n_cores - load_avg_15min" [2]
as the number of threads.

Some have argued online that 15min is too long, while
some have argued 1min would be too short. It seems these
opinions depend on the developers' perception of the
"expected task" duration.

With that in mind, I would like to suggest (and in the
future, implement) three approaches to ease this:

1. Notice that if you have two parallel blocks, one after
another, and the first one runs for <1s, asking the 15min
average from the kernel again would yield almost the same
answer, and the overhead of this call would not have been
used well.  My suggestion here is to memoize loadavg  and
the measurement's timestamp. Then, we would only request
it again if at least 30s have passed. [3]

2. If we memoize as point 1 above suggests, I would
also say that GOMP_parallel_start is not the best place
to do so. Every cycle we lose here is a cycle that N
threads could be using in their own CPU cores. Perhaps
it would be opportunistic to get this info in
GOMP_parallel_end, since we are more likely to be waiting
for other threads to finish.

3. Given the way that the sysinfo syscall works, if we
read the 15min average, we also have the 1min and 5min
averages as well, and we could also get the program
running time with minimal overhead. Given these four
variables, we are tasked to come up with some heuristic
to achieve good a performance/resource balance. libgomp's
current answer is "n_cores - load_avg_15min".
I would suggest:

3.1 If the 1min average is lower than the 15min's, and
the program is running for less than 1min (such as most
command line tools), we could interpret that the user
just finished a heavy workload and that we can use more
threads than the 15min avg would suggest.

3.2 If the program has been running for some minutes,
instead of using "n_cores - load", we could use
"n_cores - load + 1", since the current thread will
work as well, and might as well have been counted in
the average.

Benchmarking these approaches seems fun.

Let me know what you think, or if there is already work
being done on this PR.

Best regards,
Léo


[1]
- https://github.com/opencv/opencv/issues/25717
- https://github.com/numba/numba/issues/10454
- https://stackoverflow.com/questions/78584145/why-does-my-openmp-app-sometimes-use-only-1-thread-sometimes-3-sometimes-all-c/78584146

[2]  Not quite the number of cores, but
illustrative enough for an e-mail.

[3] If your program has very small tasks,
the overhead of many "getloadavg" calls might
be noticeable. In my computer, it seems to take
north of a microsecond, although the test was
very barebones.

Reply via email to