Python’s multiprocessing module gives developers the ability to write “multi-threaded” applications in spite of the infamous Global Interpreter Lock. Although multiprocessing requires more overhead, it enables more efficient use of a CPU’s cores, especially for compute-bound applications.
One of the recent tasks I had to complete for work was a high-performance application involving Ethernet streaming, GPU computation, CPU computation, file I/O, and a GUI. I had initially implemented all of this on a single thread, but moved on to use multiple Python processes once it became clear that a single process wasn’t going to cut it. As such, I ended up using a “pipelined” architecture, where each process was assigned a one of many tasks. Messages were passed between processes using the multiprocessing.Queue abstraction. Re-architecting the code in such a manner resulted in a significant runtime performance boost, but not nearly as much as I had hoped for (despite utilizing all four of my computer’s cores at near 100%).
Enter niceness, a process-specific value that signals how many cycles an active process should get relative to other active processes on an overburdened CPU or I/O bus. Note that a process’s niceness is different from its priority1 – lower niceness values get higher priority and vice versa. By increasing the niceness of the least demanding Python process, I was able to increase the application’s overall performance without having to add more pipelines or upgrade the hardware:
import os def set_niceness(value): niceness = os.nice(0) os.nice(value - niceness)
Thus, to decrease a process’s priority (increase niceness) to its maximum value, all I had to do was call
set_niceness(5). One caveat – regular users cannot decrease a process’s niceness without updating the machine’s conf files, even after increasing it from its default value:
>>> os.nice(-19) Traceback (most recent call last): File "
", line 1, in OSError: [Errno 1] Operation not permitted
To get around this, I added the following line to
/etc/security/limits.conf (YMMV, I’m using Ubuntu):
liuf - priority -20
liuf with your username and you’re good to go. Note that this has the side effect of setting the default process niceness to
-20, so use with caution.
1Linux processes also have a “priority” value, but I won’t get into that in this post. I believe it’s possible for a user to directly set a process’s priority as well, but I wouldn’t recommend it unless you a familiar with Linux schedulers. I, for one, am not.