A few days ago I discovered
cpulimit. It's a great tool that nicely (haha) complements
nice is normally used to reduce the amount of CPU a process uses by changing it priority, a
niced process can still end up using more CPU than you want, and will of course use all that it wants if nothing with a higher priority comes along.
But sometimes you want to restrict a process to using no more than some particular fraction of CPU time regardless of priority. A good example is when you don't want those noisy PC fans to kick in and you don't care how long a job takes because whether it finishes in 15 minutes or 8 hours it's still going to finish while you're asleep.
cpulimit is perfect for this, and is simple to use. An invocation like this:
cpulimit -l 50 somecommand ...
somecommand but restrict it to only use 50% of a CPU. It does this by forking a watchdog process that periodically checks to see how much work
somecommand is doing, and if it's used too much briefly pauses it by sending the
STOP signal. After a little while it will un-pause it with the
CONT signal. Of course, because the load on your machine from other processes will never quite be constant,
cpulimit rarely hits the target exactly, but it gets close enough.
But I wanted to tweak it a bit. I wanted to be able to interactively "turn the volume knob" so that I could give
somecommand more or less CPU whenever I fancied. The result is a pull request which unfortunately is unlikely to ever get merged, as the original author hasn't touched the project in years, but if any of you want the nifty new feature applying the patch and building your own custom
cpulimit is pretty easy.
How the patch works is simple. It installs signal handlers for
SIGUSR2 which respectively increase and decrease the CPU allocation by 1%. Want to turn it up by 50%? Just write a little shell loop to send the signal 50 times:
for i in $(seq 1 50); do kill -SIGUSR1 $pid; done
Determining which process to send the signal to is a bit tricky, as there are two
cpulimit processes running. There's the first one, which is just waiting in the background for
somecommand to finish, then there's the watchdog that got forked off. It's the watchdog you want to send the signals to. You can tell which is the watchdog as it will generally have a higher PID and be using a little bit of CPU. If you are
cpulimiting multiple processes then you can tell which watchdog is related to which process because the watchdog will have the command and its arguments on its command line. For example:
$ ps aux|grep ffmpeg|grep -v grep
david 90311 103.3 0.7 36105472 485448 s011 T 6:42pm 1:07.89 ffmpeg ...
david 90312 5.6 0.0 34221044 828 s011 S 6:42pm 0:02.82 cpulimit -l 100 ffmpeg ...
david 90310 0.0 0.0 34122740 796 s011 S 6:42pm 0:00.01 cpulimit -l 100 ffmpeg ...
You can see here that I asked
cpulimit to allow
ffmpeg to only use 1 CPU of the several available on this machine (ie to use 100% of a CPU - on modern machines the maximum allowed is the number of CPU cores * 100%). My shell accordingly started process 90310 which forked and execed
ffmpeg with pid 90311 and forked the watchdog process as pid 90312. The watchdog is using a little bit of CPU. It is therefore to process 90312 that I should send signals.
$ for i in $(seq 1 400); do kill -SIGUSR1 90312; done
That will send the "turn it up a bit" signal 400 times, so
ffmpeg is now limited to at most 500% of a CPU, and a few moments later:
$ ps aux|grep ffmpeg|grep -v grep
david 90311 497.0 0.8 36105472 507160 s011 T 6:42pm 12:42.37 ffmpeg ...
we can see that
ffmpeg is running a lot harder, now taking just under 500% of the CPU.