The Linux Scheduler: A Decade of Wasted Cores [pdf]
ece.ubc.ca
ece.ubc.ca
# ./runqlat
Tracing run queue latency... Hit Ctrl-C to end.
^C
usecs : count distribution
0 -> 1 : 233 |*********** |
2 -> 3 : 742 |************************************ |
4 -> 7 : 203 |********** |
8 -> 15 : 173 |******** |
16 -> 31 : 24 |* |
32 -> 63 : 0 | |
64 -> 127 : 30 |* |
128 -> 255 : 6 | |
256 -> 511 : 3 | |
512 -> 1023 : 5 | |
1024 -> 2047 : 27 |* |
2048 -> 4095 : 30 |* |
4096 -> 8191 : 20 | |
8192 -> 16383 : 29 |* |
16384 -> 32767 : 809 |****************************************|
32768 -> 65535 : 64 |*** |
I'll also use metrics that sum it by thread to estimate speed up (which helps quantify the issue), and do sanity tests.Note that this isolates one issue -- wait time in the scheduler -- whereas NUMA and scheduling also effects memory placement, so the runtime of applications can become slower with longer latency memory I/O from accessing remote memory. I like to measure and isolate that separately (PMCs).
So I haven't generally seen such severe scheduling issues on our 1 or 2 node Linux systems. Although they are testing on 8 node, which may exacerbate the issue. Whatever the bugs are, though, I'll be happy to see them fixed, and may help encourage people to upgrade to newer Linux kernels (which come with other benefits, like BPF).
None of them should affect a 1-node system, and the "Scheduling Group Construction" bug requires a multi-level node hierarchy.
cat ${node_file} | xargs -I {} -P ${NPROCS} -n 1 /usr/bin/numactl -N ${node_num} -l ${script_file} {} $*
I know about how many parallel procs I can run on a single node, and I've got something else that scrapes numactl -H for node count. arch/x86/kernel/smpboot.c: In function ‘set_cpu_sibling_map’:
arch/x86/kernel/smpboot.c:452:16: error: ‘sched_max_numa_distance’ undeclared (first use in this function)
&& sched_max_numa_distance == -1)
^
arch/x86/kernel/smpboot.c:452:16: note: each undeclared identifier is reported only once for each function it appears in
make[2]: *** [arch/x86/kernel/smpboot.o] Error 1
Really wish they'd post this to lkml, where the engineers who wrote the scheduler, and engineers to regularly performance test Linux, can reply.This should be posted to lkml, where many others can test it. If there are wins to be had on larger node systems, they'll be identified and this will be fixed.
Just testing one benchmark will not show it, unless you have something else running too.
They ought to be posting it to lkml, where many engineers regularly do performance testing. I've looked enough to think that my company isn't really hurt by this.
Basically they run R, a single threaded statistics tool which is setup to hog a core, and in some other cgroup a wildly multithreaded tool. If you have a NUMA system (check with `lstopo`) then it's possible that the scheduler thinks the many tasks in one domain of cores is balanced with just R on one core of another domain. Meaning you can have several (ex: 7 out of 8) cores idle. It has to do with the way hierarchical rebalancing is coded, and that their 8x 8-core AMD machine has a deep hierarchy.
And another, which highlights why there might be many more unearthed bugs, and would probably go unnoticed. "I suspect that making the scheduler use per-CPU queues together with some inter-CPU load balancing logic is probably trivial . Patches already exist, and I don’t feel that people can screw up the few hundred lines too badly"
Looking at the bigger picture in general, this again shows that getting software right is not easy. Now and then you still hear about the bugs popping up in the code which is very core to an OS. One I can recall is a decade old TCP bug which Google fixed last year[1].
[1] http://bitsup.blogspot.sg/2015/09/thanks-google-tcp-team-for...
"this again shows that getting software right is not easy."
Not easy, yes, but in my experience getting software right isn't too difficult. What seems impossibly hard is keeping software right, maintaining correctness in the presence of new requirements, compatibility constraints and just sheer caring about different things than we used to[1]. At some point I suspect you'd get a better design by rewriting things from scratch. But the way we do development makes rewriting stressful and error-prone, leading to the repeated arguments about whether rewriting is good or bad[2]. The question isn't whether rewriting is good or bad. Software is the most malleable medium known to man, and we should do all we can to embrace that essential malleability that is its primary characteristic and boon. The question is how not to mess up rewriting, how to keep our clay from gradually getting more and more brittle under the weight of added constraints.
(I work on this problem: https://github.com/akkartik/mu/blob/7f5a95cdbe/Readme.md. It's been 31 days since I last got on my soapbox in this forum: https://news.ycombinator.com/item?id=11285529.)
[1] "The future is a disagreement with the past about what is important." http://carlos.bueno.org/2010/10/predicting-the-future.html
[2] Against rewrites: http://www.joelonsoftware.com/articles/fog0000000069.html; https://steveblank.com/2011/01/25/startup-suicide-%E2%80%93-...; https://basildoncoder.com/blog/pg-wodehouse-method-of-refact.... Pro-rewrite seems the minority position: http://brettweigl.tumblr.com/post/48385335083/what-can-produ...; http://building.wanelo.com/2012/09/14/the-big-switch-how-we-...; http://alicebob.cryptoland.net/why-i-rewrote-quivi-from-scra... (with many apologies). And yet we've seen several major rewrites: Netscape (for all Joel Spolsky's criticism above, would Firefox had been possible without the rewrite? Echoing Jeff Bezos's dictum, open source projects can afford to be misunderstood for a long time), Python 3 [edit: no, Python 3 was not a rewrite as I was corrected below], Perl 6, Angular 2. A more balanced viewpoint: https://blog.learningbyshipping.com/2013/04/02/surviving-leg....
But why did Python 3 take so long to release, then?
- time for things to settle and eventual mistakes to be fixed (3.0, 3.1, 3.2) while the 2.x line lives
- lockstep 2.x versions that include stdlib and language (vai __future__) feature backports from 3.x line (2.6, 2.7) to get maximum portability and minimum tech debt for new code written on 2.x line.
- finally, impetus to transition with non-backported new language and stdlib features (3.4, maybe 3.3 already)
and of course, letting time for lib authors to port their existing code.
Never has it been the plan for people to instantly port existing, non-lib code to python 3.0 (or even 3.1 or 3.2), and never has it been so that python 3.x instantly obsoletes the 2.x line, and in fact, quite the opposite.
So people that like the new-n-shiny and blindly jump onto the latest version bandwagon were definitely in for the roughest ride, and gave Python 3 the bad rep we know.
I honestly prefer the way this transition has been handled rather than the "hey everything is compatible except it's not and things are subtly breaking all around" attitude from Ruby which I have to deal with now.
† the 10 year figure was somewhere in a ML that I can't seem to get a hold on back when I was working with Python and having to plan for this transition.
[PEP 3000]: https://www.python.org/dev/peps/pep-3000/
This reminds me of Brian Cantril's remarks in a talk (sorry, can't remember which one) about Solaris and DTrace. For the first time ever, DTrace gave them the ability to look into the live performance of low-level OS code and they found a lot of pathological worst-case behavior that no one suspected beforehand. No matter how good your team is it's really hard to accurately predict how a system will behave, measurement is key.
However, their premise of "just wake the idle cores as soon as there are threads to run" can actually be harmful in some real-life scenarios, especially regarding the additional power use and the slowdown introduced by switching from the filled to the empty caches.
Intuitively, as they demonstrate the "speedup" on some specific programs using some specific patterns I'd expect that some specific programs and patterns exist that don't necessary benefit from every change they present.
And yes, that proof probably will be hairy, but that's precisely the reason it has to happen.
Also, in the likely case that the proof is for a limited domain ("at most 1000 CPUs", "level 1 cache shared between the same CPUs as the level 2 cache"), I think the scheduler should test for that at boot time and strongly consider panicking the kernel if it doesn't apply.
Unidirectional interprocess communication forces this issue to come up frequently. The pattern "send on pipe A, wait for pipe B" to a process that is waiting on pipe A and will reply on pipe B implies that, for a very short period between the send and the wait, there are two processes ready to run. Starting one of them on another CPU is suboptimal, and waking up a sleeping CPU is even worse. You're really doing an interprocess subroutine call, but the OS does not know this. (As I point out occasionally, QNX, with MsgSend/MsgReceive and a scheduler that understands them, does this much better.)
Moreover, since (several of) the patches seem to rip out a bunch of logic in favor of very simple logic, they would probably be contentious without broad testing and probably a runtime option to configure the behavior.
I only mention this as a historical case that has remained in my memory. Maybe Linus is willing to revisit the issue, I don't follow LKML.
[1] https://en.wikipedia.org/wiki/Con_Kolivas
[2] http://ck-hack.blogspot.com/2015/12/bfs-467-linux-43-ck3.htm...
It'd be interesting to see if that branch exhibit the same behavior and issues.
Wish they'd look at disk I/O next, there are some problems there that are hard to describe other than anecdotically: e.g. my system runs on a SSD and periodically copies data to a HDD with rsnapshot. When rsnapshot runs rm on the HDD things freeze for a moment, (even switching windows in X) although the only thing using the HDD is that rm ...
E.g. this is just being readied: http://blog.cerowrt.org/post/fq_codel_on_ath10k/
(also note higher throughput as result)
If this result is true, our Linux machines have been wasting 13-24% of our silicon and energy for years (that number is for "typical Linux workloads") because the scheduler fails to fulfill its most basic duty.
The quotes from Linus in the paper just twist the knife.
[1] - http://csl.stanford.edu/~christos/publications/2015.heracles...
"Resulting performance degradations are in the range 13-24% for typical Linux workloads, and reach 138× in some corner cases. Energy waste is proportional."
Do you run on a server? Has it been made in the last 3 - 5 years? Then you are running a NUMA system.
* is the system compute bound or io bound? - in the latter case the waste will be smaller.
* How does this compare to other OSes? If it the most efficient available kernel for your workload, compared to other options, it is questionable to say it fails.
I'd find it hard to believe we missed a 13-24% win on all Linux systems anyway. Linux undergoes intense performance analysis, including checking for poor scheduling behavior.
This sounds very much like a bug on 8 node NUMA systems. There's been lots of scheduling bugs over the years.
So can someone who knows about linux kernel internals explain the impact of this research? I read the abstract and some of the paper and it sounds very promising - like we may get significantly more performance out of our cores for multi-threaded or multi-process applications.
However, "performance of Node, PHP and Python" is a sensible goal in its own right, and I disagree with the implication of your comment, and that of sister comments, that it is not a sensible goal. There's a lot of useful code written in Node, PHP and Python, and moreover, this might remain true for a long while because "something more performance oriented" is likely to be less programmer productivity oriented in that a correct, easy to use program or library will take more time to get done. Also, Node and Python specifically can be damn fast (numpy for instance is unbeatable for large linear algebra programs, because it uses optimized libraries under the hood, etc. etc.)
And some things simply can't be done in a satisfactory fashion in anything but a dynamic language, any more than you can get Apache to run on a GPU. "Dynamic" is a feature, not just a source of overhead.
So "a performance-obsessed scripting language developer" is a perfectly fine way to describe oneself IMO.
But when writing Python modules in C, you have control over acquiring and releasing of the GIL, so before starting some long running operation, you give up the lock.
Node, AFAIK, uses several OS-level threads under the hood for disk I/O. And with PHP, a web server probably will run multiple threads for handling requests concurrently.
So the impact might not be as big as for performance-oriented code in C/C++, but it is not necessarily nil, either.
Also, in my understanding TFA applies to multiple processes just as much as multiple threads.
To clarify- languages are never by definition, single threaded. The reference implementations largely are.
Some background- in Python's case, PyPy supports STM which removes the global interpreter lock, while retaining backwards compatibility with existing code.
The answer to your question is no. All 3 are not single threaded.
But you're always going to win this argument by suggesting a lower level solution until we arrive at coding assembly optimized for a specific bare metal.
> who uses things like node, php, python, etc.
pick one
Off-topic, but high-performance long running processes are mainly programmed in C, C++ & Java. Maybe stuff like Rust and Swift in the future. Fortran if you are doing mathematical computation, but then you'd probably already use it if you need it.
For what I estimate that you mean with high traffic on PHP or node systems on multiple servers, probably you want to look at Elixir and it's Phoenix web framework. It's more appropriate for responsiveness (as in low latency). And less boilerplate than Java. |> http://www.phoenixframework.org/docs/overview
I don't quite understand the choice for picking the core that was idle for the longest time. I think they use that as a predictor of future load of the CPU, and scheduling based on future load see,s a good idea, but I think this could lead to cases where it prevents one of more CPUs to go to low power states when the system doesn't have enough work to keep all its cores fully occupied.
(Edit: how did I overlook the following paragraph, where they discuss this issue?)
Also, in general, I think they too easily call changes that remove the corner cases they find fixes. Chances are that they introduce other corner cases, either on workloads they didn't test or on hardware they didn't test (caveat: I know very little about the variation in hardware that is out there)
Though from my understanding of the patch they walk over all CPUs which is not precisely constant time, and it doesn't seem to be just the list of idle CPUs; for_each_online_cpu(_cpu): https://github.com/jplozi/wastedcores/blob/master/patches/ov...
That said from what I grasp of the code it also doesn't bail out when power management is enabled, so maybe the public code or the paper are not at the same stage of development.
There might be newer multistage power down but to wear level and use all cores, this is the right algorithm.
I thought it was either a bug with the TCP polling mechanism or with the Linux OS scheduler itself. It's good that this issue is finally getting some attention.
Probably just before rcu_read_unlock() in that function: http://lxr.free-electrons.com/source/kernel/sched/fair.c#L51...
I do not believe it, sorry. Troll paper.
Check out the massive indentation change in this patch which obscures the changes being made:
https://github.com/jplozi/wastedcores/blob/master/patches/sc...
Good grief.
Anyway that's not basis enough to invalidate their claims. They're published in EuroSys [1]. Is that not a reputable journal/conference?
Plus this dude has a sweet CV layout [2]. I'm inclined to believe him on that basis alone.
The idea that the kernel runs idle tasks on cores while actual tasks are runnable (and that this situation persists for seconds) is ridiculous. It's such a gaping problem, that everyone doing anything with Linux anywhere would be running into it on a regular basis.
Think about all the people out there who carefully profile the performance of their multi-threaded apps. Like all of them wouldn't notice missing seconds of execution?
That sounds like a generalization about the Linux kernel scheduler for an entire decade of kernel versions =) (just kidding haha!)
That's what I call vivid language!
Presumably things started as sysv scripts or even docker containers don't have this problem?
In figure 1 the levels/shades represent distance from node/socket 1, darker being closer. So node 1 is distance 0 from itself, two other nodes are distance 1, and one node is distance 2.
https://events.linuxfoundation.org/images/stories/slides/lfc...
Also, what CPUs besides Intel's and IBM's use SMT?
Itanium also supports some form of SMT, I think, although I am not sure if anyone actually uses those.
Yeah, an oddly thought out design for sure. The idea seems to make sense to me but execution wasn't good enough I guess.
E.g. the the two cores in the dark grey box can steal work from each other. But they will only see load averages of the neighbouring domain. In certain cases the current scheduler calculates the load figures sort of odd, so the idle core decides that a neighboring overloaded 'scheduling domain' is not overloaded.
Machine learning sounds like it would add even more complexity but perhaps I just fail to see why machine learning is a good idea here. I don't see how you can predict workloads based off historical data and if you're going to try to predict workloads based off binaries, the overhead would likely outweigh any benefits you would get.
I'll admit I am no expert in machine learning but I have a hard time understanding why you would look at this problem and think machine learning is the solution.
... Think about that part, then:)
There was a "genetic algorithm" patch years back, it basically identified busier processes and preferred to give them cycles. It kind of sucked as less busy processes would starve. We are at an interesting place now, we have a good scheduler with o(1) semantics and it's fair, but it's leaving cycles on the floor on certain hardware with certain workloads.
I would think a machine learning approach would be more expensive and it could potentially be difficult to explain why it was doing a certain thing.
There is a long held belief that a good Linux scheduler shouldn't have tuning knobs and we don't need to select the scheduler at build time. I could see that belief coming to an end as we run on smart phones up to supercomputers with performance and energy being key issues on both ends of the spectrum. Some of the more exotic heterogenous hardware is becoming very popular too and that may beg the question more.
Servers usually have super long uptimes, too. So the only valid criticism of JIT, which is warm up, will be a nonfactor!
(big /s if not obvious.
JIT and ML evangelists make me sick.)
Also, Rust's regex engine (which is based on a lazy DFA) is giving PCRE2's JIT a run for its money. ;-) https://gist.github.com/anonymous/14683c01993e91689f7206a186...
I've used Rust's regex engine before, it's very promising. It's really neat how the regex itself is fully compiled.
This wouldn't be practical unless it could be optimized "to the nines" to be very fast. The previous Linux scheduler ran in O(1), the new Completely Fair Scheduler runs in O(log N). The scheduler has to be able to run every CPU tick, so it has to be fast. A machine learning based scheduler does not sound like it could be made to be that fast. To put this into perspective, on a regular x86_64 the CPU tick in Linux 1000Hz.
I think they're better off if they post a RFC patch to LKML as it exists to facilitate discussion and testing.
Software engineers are still obsessed with squeezing every last drop of performance from a single core, adding multicore or distributed load support as an afterthought.
Sorry, it doesn't work this way anymore. There will be no more single core performance increases — laws of physics forbid it. Instead, we will see more and more cores in common CPUs (64, then 256, then 1024 — then it will be merged with GPGPUs and FPGAs with their stream processing approach).
Learn distributed programming or perish.
Or put in another way: If we want more raw processing power, we need more cores, but we don't want more cores because software is optimised for a single core.
Kinda looking forward to lowRISC with minion cores, though - if we ever ditch x86 for games.
Multicore ARM is still inferior to multicore x86.
assuming which software load?
You're in luck:
http://www.theregister.co.uk/2016/03/14/intel_xeon_fpga/
Intel's purchase of Altera is sure to lead to all kinds of innovation in this area. This is hopefully only the start.
To me it just seemed silly that we'd continually upgrade to get more dedicated circuitry for things like low-power video decoding. I know programming an FGPA still wouldn't be as efficient as it could be, but I think in the future it might be nice to have an FGPA available for adding things like x265 hardware decoding a year after purchase.
What I'd REALLY love to see is an FPGA in a Chromebook for students. School will be so awesome in the next decade.
It is $CURRENT_YEAR, yes.
> Software engineers are still obsessed with squeezing every last drop of performance from a single core, adding multicore or distributed load support as an afterthought.
Normally I'd agree, but if you had bothered reading the abstract the performance losses were not negligible and the kernel scheduler was responsible for the losses. This has nothing to do with application programmers not understanding "distributed programming" as you put it.
From the article:
> As a central part of resource management, the OS thread scheduler must maintain the following, simple, invariant: make sure that ready threads are scheduled on available cores.
> As simple as it may seem, we found that this invariant is often broken in Linux. Cores may stay idle for seconds while ready threads are waiting in runqueues.
> In our experiments, these performance bugs caused many-fold performance degradation for synchronization-heavy scientific applications, 13% higher latency for kernel make, and a 14-23% decrease in TPC-H throughput for a widely used commercial database.
This is just plain wrong. Look at any Intel CPU generation and you will find that the new one is faster than the old one clock-to-clock.