Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to limit I/O consumption of Python processes (possibly using ionice)?

I would like a particular set of Python subprocesses to be as low-impact as possible. I'm already using nice to help limit CPU consumption. But ideally I/O would be limited as well. (If skeptical, please humor me and assume there is value in doing this; it doesn't matter how long they take to run, there can be a lot of them, and there is higher-priority stuff (usually) going on on the same machine, etc.)

One possibility appears to be ionice. Are there any existing Python packages for invoking ionice (Google didn't turn up anything)? It wouldn't be difficult to write code to simply run the ionice command; but I'd prefer to avoid writing code that someone else has written/tested; sometimes there are subtle edge cases, etc. And, is there just a better way to limit I/O consumption?

The man page for ionice suggests that the ionice value can be affected by the nice value, but running this Python 2.6 script appears to disprove that, even for child processes where the nice value is inherited:

#!/usr/bin/env python

import os
import multiprocessing

def print_ionice(name):
    print '*** ', name, ' ***'
    os.system("echo -n 'nice: '; nice")
    os.system("echo -n 'ionice: '; ionice -p%d" % os.getpid())

for niced in (None, 19):
    if niced: os.nice(niced)
    print '**** niced to: ', niced, ' ****'
    print_ionice('parent')
    subproc = multiprocessing.Process(target=print_ionice, args=['child'])
    subproc.start()
    subproc.join()

Which has the following output:

$ uname -as
Linux x.fake.org 2.6.27-11-server #1 SMP Thu Jan 29 20:13:12 UTC 2009 x86_64 GNU/Linux
$ ./foo.py
**** niced to:  None  ****
***  parent  ***
nice: 0
ionice: none: prio 4
***  child  ***
nice: 0
ionice: none: prio 4
**** niced to:  19  ****
***  parent  ***
nice: 19
ionice: none: prio 4
***  child  ***
nice: 19
ionice: none: prio 4
like image 385
Jacob Gabrielson Avatar asked Mar 31 '09 18:03

Jacob Gabrielson


Video Answer


2 Answers

psutil exposes this functionality (python 2.4 -> 3.2):

import psutil, os
p = psutil.Process(os.getpid())
p.ionice(psutil.IOPRIO_CLASS_IDLE)

Also, starting from Python 3.3 this will be available in python stdlib as well: http://bugs.python.org/issue10784

like image 164
Giampaolo Rodolà Avatar answered Oct 19 '22 03:10

Giampaolo Rodolà


Hm.

As a start pointer, you should find what syscall number are the ioprio_set and ioprio_get system calls in your kernel. I'd suggest you check in /usr/include/asm/unistd_32.h or /usr/include/asm/unistd_64.h, depending on your kernel arch; if not there, start with the suggestion of the syscall(2) man page, which should be /usr/include/sys/syscall.h and work your way down includes.

Given that, you should use ctypes, à la:

def ioprio_set(which, who, ioprio):
    rc= ctypes.CDLL('libc.so.6').syscall(289, which, who, ioprio)
    # some error checking goes here, and possibly exception throwing

That's it, more or less. Have fun :)

like image 30
tzot Avatar answered Oct 19 '22 04:10

tzot