sc_profiling

sc_profiling

Profiling and CPU/memory management functions.

Highlights

Classes

Name Description
LimitExceeded Custom exception for use with the sc.resourcemonitor() monitor.
cprofile Function profiler, built off Python’s built-in cProfile
profile Profile the line-by-line time required by a function.
resourcemonitor Asynchronously monitor resource (e.g. memory) usage and terminate the process
tracecalls Trace all function calls.

LimitExceeded

sc_profiling.LimitExceeded()

Custom exception for use with the sc.resourcemonitor() monitor.

It inherits from MemoryError since this is the most similar built-in Python except, and it inherits from KeyboardInterrupt since this is the means by which the monitor interrupts the main Python thread.

cprofile

sc_profiling.cprofile(
    sort='cumtime',
    columns='default',
    mintime=0.001,
    maxitems=100,
    maxfunclen=40,
    maxpathlen=40,
    use_ms=None,
    stripdirs=True,
    show=True,
)

Function profiler, built off Python’s built-in cProfile

Note: sc.profile() shows the time taken by each line of code, in the order the code appears in. sc.cprofile() shows the time taken by each function, regardless of where in the code it appears.

The profiler can be used either with the enable() and disable() commands, or as a context block. See examples below for details.

Default columns of output are:

- 'func': the function being called
- 'cumpct': the cumulative percentage of time taken by this function (including subfunctions)
- 'selfpct': the percentage of time taken just by this function (excluding subfunctions)
- 'cumtime': the cumulative time taken by this function
- 'selftime': the time taken just by this function
- 'calls': the number of calls
- 'path': the file and line number

Parameters

Name Type Description Default
sort str the column to sort by (default “cumpct”) 'cumtime'
columns str what columns to show; options are “default” (above), “brief” (just func, cumtime, selftime), and “full” (as default plus percall and separate line numbers) 'default'
mintime float exclude function times below this value 0.001
maxitems int only include up to this many functions in the output 100
maxfunclen int maximum length of the function name to print 40
maxpathlen int maximum length of the function path to print 40
use_ms bool if True, convert to milliseconds; if None, scale if and only if all durations are <1 second None
stripdirs bool whether to strip folder information from the file paths True
show bool whether to show results of the profiling as soon as it’s complete True

Examples:

import sciris as sc
import numpy as np

class Slow:

    def math(self):
        n = 1_000_000
        self.a = np.arange(n)
        self.b = sum(self.a)

    def plain(self):
        n = 100_000
        self.int_list = []
        self.int_dict = {}
        for i in range(n):
            self.int_list.append(i)
            for j in range(10):
                self.int_dict[i+j] = i+j

    def run(self):
        self.math()
        self.plain()

# Option 1: as a context block
with sc.cprofile() as cpr:
    slow = Slow()
    slow.run()

# Option 2: with start and stop
cpr = sc.cprofile()
cpr.start()
slow = Slow()
slow.run()
cpr.stop()
  • New in version 3.1.6.
  • New in version 3.2.4: “use_ms” argument

Methods

Name Description
disp Display the results of the profiling; arguments are passed to to_df()
parse_stats Parse the raw data into a dictionary
start Start profiling
stop Stop profiling
to_df Parse data into a dataframe
disp
sc_profiling.cprofile.disp(*args, **kwargs)

Display the results of the profiling; arguments are passed to to_df()

parse_stats
sc_profiling.cprofile.parse_stats(stripdirs=None, force=False)

Parse the raw data into a dictionary

start
sc_profiling.cprofile.start()

Start profiling

stop
sc_profiling.cprofile.stop()

Stop profiling

to_df
sc_profiling.cprofile.to_df(
    sort=None,
    mintime=None,
    maxitems=None,
    maxfunclen=None,
    maxpathlen=None,
    columns=None,
)

Parse data into a dataframe

See class docstring for arguments

profile

sc_profiling.profile(
    run,
    follow=None,
    private='__init__',
    include=None,
    exclude=None,
    unwrap=True,
    skipzero=False,
    do_run=True,
    verbose=True,
    *args,
    **kwargs,
)

Profile the line-by-line time required by a function.

Interface to the line_profiler library.

Note: sc.profile() shows the time taken by each line of code, in the order the code appears in. sc.cprofile() shows the time taken by each function, regardless of where in the code it appears.

Parameters

Name Type Description Default
run function The function to be run required
follow function The function, list of functions, class, or module to be followed in the profiler; if None, defaults to the run function None
private bool / str / list if True and a class is supplied, follow private functions; if a string/list, follow only those private functions (default '__init__') '__init__'
include str if a class/module is supplied, include only functions matching this string None
exclude str if a class/module is supplied, exclude functions matching this string None
unwrap bool if true (default), then unwrap functions wrapped by decorators (otherwise, the decorator is profiled) True
skipzero bool skip functions with 0 time (i.e. that were not run); default false (i.e. do include them) False
do_run bool whether to run immediately (default: true) True
print_stats bool whether to print the statistics of the profile to stdout (default True) required
verbose bool list the functions to be profiled True
args list Passed to the function to be run ()
kwargs dict Passed to the function to be run {}

Returns

Name Type Description
LineProfiler (by default, the profile output is also printed to stdout)

Example:

def slow_fn():
    n = 10000
    int_list = []
    int_dict = {}
    for i in range(n):
        int_list.append(i)
        int_dict[i] = i
    return

class Foo:
    def __init__(self, a=0):
        self.a = a

    def outer(self):
        for i in range(100):
            self.inner()

    def inner(self):
        for i in range(1000):
            self.a += 1

# Profile a function
sc.profile(slow_fn)

# Profile a class or class instance
foo = Foo()
sc.profile(run=foo.outer, follow=foo)

# Profile the constructor for Foo
f = lambda a: Foo(a)
sc.profile(run=f, follow=Foo.__init__, a=10) # "a" is passed to the function
  • New in version 3.2.0: allow class and module arguments for “follow”; “private” argument
  • New in version 3.2.2: converted to a class
  • New in version 3.2.4: “merge” method, “unwrap” argument

Methods

Name Description
disp Display the results of the profiling
merge Allow multiple profilers to be combined (to be able to do combined stats)
parse_follow Do processing on the functions
plot Plot the time spent on each function.
run Run profiling
sort Sort or unsort by time.
disp
sc_profiling.profile.disp(bytime=1, maxentries=10, skiprun=False)

Display the results of the profiling

merge
sc_profiling.profile.merge(other, inplace=False, swap=False, overwrite=True)

Allow multiple profilers to be combined (to be able to do combined stats)

Parameters
Name Type Description Default
other sc.profile the other sc.profile instance to merge required
inplace bool if True, modify this instance in place False
swap bool if True, put “other” first False
overwrite bool if True, overwrite duplicates (with the one that took more time) True
  • New in version 3.2.4.
parse_follow
sc_profiling.profile.parse_follow(strict=False)

Do processing on the functions

plot
sc_profiling.profile.plot(
    bytime=1,
    maxentries=10,
    figkwargs=None,
    barkwargs=None,
)

Plot the time spent on each function.

Parameters
Name Type Description Default
bytime bool if True, order events by total time rather than actual order 1
maxentries int how many entries to show 10
figkwargs dict passed to plt.figure() None
barkwargs dict passed to plt.bar() None
run
sc_profiling.profile.run(disp=None)

Run profiling

Parameters
Name Type Description Default
disp bool whether to display results (self.disp()) after run; if None, use self.verbose value None
sort
sc_profiling.profile.sort(bytime=1, copy=False)

Sort or unsort by time.

Parameters
Name Type Description Default
bytime int if 1, sort by increasing time (default); if -1, sort by decreasing; if 0, do not sort by time 1

resourcemonitor

sc_profiling.resourcemonitor(
    mem=0.9,
    cpu=None,
    time=None,
    interval=1.0,
    label=None,
    start=True,
    die=True,
    kill_children=True,
    kill_parent=False,
    callback=None,
    verbose=None,
)

Asynchronously monitor resource (e.g. memory) usage and terminate the process if the specified threshold is exceeded.

Parameters

Name Type Description Default
mem float maximum virtual memory allowed (as a fraction of total RAM) 0.9
cpu float maximum CPU usage (NB: included for completeness only; typically one would not terminate a process just due to high CPU usage) None
time float maximum time limit in seconds None
interval float how frequently to check memory/CPU usage (in seconds) 1.0
label str an optional label to use while printing out progress None
start bool whether to start the resource monitor on initialization (else call start()) True
die bool whether to raise an exception if the resource limit is exceeded True
kill_children bool whether to kill child processes (if False, will not work with multiprocessing) True
kill_parent bool whether to also kill the parent process (will usually exit Python interpreter in the process) False
callback func optional callback if the resource limit is exceeded None
verbose bool detail to print out (default: if exceeded; True: every step; False: no output) None

Examples:

# Using with-as:
with sc.resourcemonitor(mem=0.8) as resmon:
    memory_heavy_job()

# As a standalone (don't forget to call stop!)
resmon = sc.resourcemonitor(mem=0.95, cpu=0.9, time=3600, label='Load checker', die=False, callback=post_to_slack)
long_cpu_heavy_job()
resmon.stop()
print(resmon.to_df())
,

Methods

Name Description
check Check if any limits have been exceeded
kill Kill all processes
monitor Actually run the resource monitor
start Start the monitor running
stop Stop the monitor from running
to_df Convert the log into a pandas dataframe
check
sc_profiling.resourcemonitor.check()

Check if any limits have been exceeded

kill
sc_profiling.resourcemonitor.kill()

Kill all processes

monitor
sc_profiling.resourcemonitor.monitor(label=None, *args, **kwargs)

Actually run the resource monitor

start
sc_profiling.resourcemonitor.start(label=None)

Start the monitor running

Parameters
Name Type Description Default
label str optional label for printing progress None
stop
sc_profiling.resourcemonitor.stop()

Stop the monitor from running

to_df
sc_profiling.resourcemonitor.to_df()

Convert the log into a pandas dataframe

tracecalls

sc_profiling.tracecalls(
    trace='<default>',
    exclude='<default>',
    regex=False,
    repeats=False,
    custom=None,
    verbose=None,
)

Trace all function calls.

Alias to sys.steprofile().

Parameters

Name Type Description Default
trace str / list / regex the module(s)/file(s) to trace calls from (’’ matches all, but this is usually undesirable) '<default>'
exclude str / list / regex a list of modules/files to exclude (default excludes builtins; set to None to not exclude anything) '<default>'
regex bool whether to interpret trace and exclude as regexes rather than simple string matching False
repeats bool whether to record repeat calls of the same function (default False) False
custom func if provided, use this rather than the built in logic for checking for matches None
verbose bool how much information to print (False=silent, None=default, True=debug) None

Examples:

import mymodule as mm

# In context block
with sc.tracecalls('mymodule'):
    mm.big_operation()

# Explicitly
tc = sc.tracecalls('*mysubmodule*', exclude='^init*', regex=True, repeats=True)
tc.start()
mm.big_operation()
tc.stop()
tc.df.disp()
  • New in version 3.2.0.
  • New in version 3.2.1: “custom” argument added; “kwargs” removed

Methods

Name Description
check_expected Check function calls against a list of expected function calls.
disp Display the results
start Start profiling
stop Stop profiling
to_df Convert to a dataframe; if repeats=True, also count repeats
check_expected
sc_profiling.tracecalls.check_expected(expected, die=False)

Check function calls against a list of expected function calls.

Parameters
Name Type Description Default
expected set / list / any if a list of set of strings, check those function names; if object(s) or classes are supplied, check each method required
die bool raise an exception if any expected function calls were not called False

Example:

# Check which methods of a class are called
with sc.tracecalls() as tc:
    my_obj = MyObj()
    my_obj.run()

expected = tc.check_expected(MyObj) # Equivalent to tc.check_expected(my_obj)
print(expected)
disp
sc_profiling.tracecalls.disp(maxlen=60)

Display the results

start
sc_profiling.tracecalls.start()

Start profiling

stop
sc_profiling.tracecalls.stop(disp=None)

Stop profiling

to_df
sc_profiling.tracecalls.to_df()

Convert to a dataframe; if repeats=True, also count repeats

Functions

Name Description
benchmark Benchmark Python performance
checkmem Checks how much memory the variable or variables in question use by dumping
checkram Measure actual memory usage, typically at different points throughout execution.
listfuncs Enumerate all functions in the supplied arguments; used in sc.profile().
mprofile Profile the line-by-line memory required by a function. See profile() for a

benchmark

sc_profiling.benchmark(
    repeats=5,
    scale=1,
    verbose=False,
    which='python, numpy',
    parallel=False,
    return_timers=False,
)

Benchmark Python performance

Performs a set of standard operations in both Python and Numpy and times how long they take. Results are returned in terms of millions of operations per second (MOPS). With default settings, this function should take very approximately 0.1 s to run (depending on the machine, of course!).

For Python, these operations are: for loops, list append/indexing, dict set/get, and arithmetic. For Numpy, these operations are: random floats, random ints, addition, and multiplication.

Parameters

Name Type Description Default
repeats int the number of times to repeat each test 5
scale float the scale factor to use for the size of the loops/arrays (default 1e3 for Python and 1e6 for NumPy) 1
verbose bool print out the results after each repeat False
which str whether to run Python tests, Numpy tests, or both (default) 'python, numpy'
parallel bool / int whether to run the tests across all cores False
return_timers bool if True, return the timer objects instead of the “MOPS” results False

Returns

Name Type Description
A dict with keys “python” and “numpy” for the number of MOPS for each

Examples:

sc.benchmark() # Returns e.g. {'python': 11.43, 'numpy': 236.595}

numpy_mops = sc.benchmark(which='numpy')
if numpy_mops < 100:
    print('Your computer is slow')
elif numpy_mops > 400:
    print('Your computer is fast')
else:
    print('Your computer is normal')

sc.benchmark(parallel=True) # Use all CPUs
  • New in version 3.0.0.
  • New in version 3.1.0: “parallel” argument; increased default scale
  • New in version 3.2.4: replaced “python” and “numpy” arguments with “which”

checkmem

sc_profiling.checkmem(
    var,
    descend=1,
    order='size',
    compresslevel=0,
    maxitems=1000,
    subtotals=True,
    plot=False,
    verbose=False,
    **kwargs,
)

Checks how much memory the variable or variables in question use by dumping them to file.

Note on the different functions:

- `sc.memload()` checks current total system memory consumption
- `sc.checkram()` checks RAM (virtual memory) used by the current Python process
- `sc.checkmem()` checks memory consumption by a given object

Parameters

Name Type Description Default
var any the variable being checked required
descend bool whether or not to descend one level into the object 1
order str order in which to list items: “size” (default), “alphabetical”, or “none” 'size'
compresslevel int level of compression to use when saving to file (typically 0) 0
maxitems int the maximum number of separate entries to check the size of 1000
subtotals bool whether to include subtotals for different levels of depth True
plot bool if descending, show the results as a pie chart False
verbose bool or int detail to print, if >1, print repr of objects along the way False
**kwargs dict passed to sc.load() {}

Examples:

import numpy as np
import sciris as sc

list_obj = ['label', np.random.rand(2483,589)])
sc.checkmem(list_obj)

nested_dict = dict(
    foo = dict(
        a = np.random.rand(5,10),
        b = np.random.rand(5,20),
        c = np.random.rand(5,50),
    ),
    bar = [
        np.random.rand(5,100),
        np.random.rand(5,200),
        np.random.rand(5,500),
    ],
    cat = np.random.rand(5,10),
)
sc.checkmem(nested_dict)

New in version 3.0.0: descend multiple levels; dataframe output; “alphabetical” renamed “order”

checkram

sc_profiling.checkram(unit='mb', fmt='0.2f', start=0, to_string=True)

Measure actual memory usage, typically at different points throughout execution.

Note on the different functions:

- `sc.memload()` checks current total system memory consumption
- `sc.checkram()` checks RAM (virtual memory) used by the current Python process
- `sc.checkmem()` checks memory consumption by a given object

Example:

import sciris as sc
import numpy as np
start = sc.checkram(to_string=False)
a = np.random.random((1_000, 10_000))
print(sc.checkram(start=start))

New in version 1.0.0.

listfuncs

sc_profiling.listfuncs(
    *args,
    private='__init__',
    include=None,
    exclude=None,
    strict=False,
)

Enumerate all functions in the supplied arguments; used in sc.profile().

If module(s) are supplied, recursively search them for functions and classes. If class(es) are supplied, search them for methods. Otherwise, search input(s) for functions.

Parameters

Name Type Description Default
args list the arguments to parse for functions; can be modules, classes, or functions. ()
private bool / str / list if True and a class is supplied, follow private functions; if a string/list, follow only those private functions (default '__init__') '__init__'
include str if a class/module is supplied, include only functions matching this string None
exclude str if a class/module is supplied, exclude functions matching this string None
strict bool if True, raise an exception if something is not a function, rather than recurse into it False
  • New in version 3.2.2.
  • New in version 3.2.4: “strict” argument

mprofile

sc_profiling.mprofile(run, follow=None, show_results=True, *args, **kwargs)

Profile the line-by-line memory required by a function. See profile() for a usage example.

Parameters

Name Type Description Default
run function The function to be run required
follow function The function or list of functions to be followed in the profiler; if None, defaults to the run function None
show_results bool whether to print the statistics of the profile to stdout True
args, kwargs Passed to the function to be run required

Returns

Name Type Description
LineProfiler (by default, the profile output is also printed to stdout)