user4979733
user4979733

Reputation: 3411

pytest: Is there a way to report memory usage of a test?

I checked the pytest documentation but I could not find anything relevant. I know pytest --durations=0 will print out the runtime for all the tests. Is there a way to get pytest to also print out the peak memory usage consumed by a function? Otherwise, I can probably just use the decoration function below. But I am wondering if there is a better way to do this.

from functools import wraps

    def mem_time(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            
            # Start of function
            r0 = resource.getrusage(resource.RUSAGE_SELF)
            t0 = datetime.datetime.now()
    
            # Call function
            status = func(*args, **kwargs)
            
            # End of function
            r1 = resource.getrusage(resource.RUSAGE_SELF)
            t1 = datetime.datetime.now()
            
            sys.stderr.write('{}: utime {} stime {} wall {}\n'.format(func.__name__,
                                                                      datetime.timedelta(seconds=r1.ru_utime - r0.ru_utime),
                                                                      datetime.timedelta(seconds=r1.ru_stime - r0.ru_stime),
                                                                      t1 - t0))
    
            sys.stderr.write('{}: mem {} MB ({} GB)\n'.format(func.__name__,
                                                              (r1.ru_maxrss - r0.ru_maxrss) / 1000.0,
                                                              (r1.ru_maxrss - r0.ru_maxrss) / 1000000.0))
            
            return status
        
        return wrapper

Upvotes: 13

Views: 11265

Answers (3)

Anton Daneyko
Anton Daneyko

Reputation: 6473

The pytest-memray plugin does it out of the box. Link: https://pytest-memray.readthedocs.io/en/latest/index.html.

Upvotes: 0

jdhalimi
jdhalimi

Reputation: 261

pytest-monitor a new awesome plugins for pytest helps to monitor resources usage, timings, memory and so on. All metrics are stored in an sqlite database for post analysis

check out pytest-monitor from pypi or conda-forge:

Example

$ pytest --db ./monitor.db

$ sqlite3 ./monitor.db

sqlite>.headers on

sqlite>.mode column

Then to show 10 most memory intensive tests:

sqlite> select ITEM, MEM_USAGE from TEST_METRICS ORDER BY MEM_USAGE DESC LIMIT 10;

ITEM|MEM_USAGE
test_api/test_data_get[/data/listdsh]|17.5703125
test_api/test_data_get[/data/listrecipients]|17.97265625
test_api/test_data_get[/data/getuserdetails]|18.125

pytest-monitor updates its SQLite database incrementally, so you might want to delete monitor.db file between test runs.

Note that if there is a memory leak in your code, the memory usage can increase test-by-test regardless of in which order you run them. Thus, you might want to run single tests, tests in different order and so on to locate the memory leak.

Upvotes: 26

kolukuri
kolukuri

Reputation: 337

Memory Profiling:

There is no plugin to get memory profiling from the pytest (As far as I know). Use the following link for memory profiling

https://github.com/fabianp/memory_profiler

Other references: https://stackoverflow.com/a/43772305/9595032

Cumulative times:

https://pypi.org/project/pytest-profiling/

But to get Cumulative times of all calls to use plugin pytest-profiling

pip install pytest-profiling

Usage:

pytest -s -v [file_name] --profile

The output will look somewhat like this

Profiling (from /home/work/project/analytics/testing/TestCases/cloud/prof/combined.prof):
Wed Nov 20 12:09:47 2019    /home/work/project/analytics/testing/TestCases/cloud/prof/combined.prof

         437977 function calls (380211 primitive calls) in 3.866 seconds

   Ordered by: cumulative time
   List reduced from 683 to 20 due to restriction <20>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    3.866    3.866 runner.py:107(pytest_runtest_call)
        1    0.000    0.000    3.866    3.866 python.py:1457(runtest)
        1    0.000    0.000    3.866    3.866 hooks.py:275(__call__)
        1    0.000    0.000    3.866    3.866 manager.py:59(<lambda>)
        1    0.000    0.000    3.866    3.866 manager.py:65(_hookexec)
        1    0.000    0.000    3.866    3.866 callers.py:157(_multicall)
        1    0.000    0.000    3.866    3.866 python.py:188(pytest_pyfunc_call)
        1    0.001    0.001    3.866    3.866 test_abc.py:46(test_abc)
        1    0.000    0.000    3.865    3.865 test_abc.py:9(run_abc_test)
        1    0.001    0.001    3.854    3.854 dataAnalyzer.py:826(sanitize_data)
        1    0.000    0.000    3.773    3.773 Analyzer.py:563(Traffic)
        1    0.000    0.000    3.772    3.772 traffic.py:83(false_alarm_checks)
        3    0.000    0.000    3.767    1.256 api.py:60(get)
        3    0.000    0.000    3.765    1.255 api.py:135(_get_from_overpass)
        3    0.000    0.000    3.765    1.255 api.py:101(post)
        3    0.000    0.000    3.765    1.255 api.py:16(request)
        3    0.000    0.000    3.762    1.254 sessions.py:445(request)
        3    0.000    0.000    3.759    1.253 sessions.py:593(send)
        3    0.000    0.000    3.757    1.252 adapters.py:393(send)
        3    0.000    0.000    3.755    1.252 connectionpool.py:447(urlopen)

Upvotes: -1

Related Questions