Skip to content

Profiling Python Code

The 'profile' module

This example shows how to use the cProfile Python module to show script performance.

At release/scripts/modules/bpy/__init__.py#L68, the function call shows:

# Initializes Python classes.
# (good place to run a profiler or trace).
utils.load_scripts()

This example shows how a the function call can be profiled.

import cProfile
cProfile.run("import bpy; bpy.utils.load_scripts()", "blender.prof")

import pstats
p = pstats.Stats("blender.prof")
p.sort_stats("cumulative").print_stats(20)

This prints the top 20 cumulative functions.

Here is an example of what the output may look like.

         45572 function calls (43669 primitive calls) in 0.243 seconds

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

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    120/1    0.002    0.000    0.243    0.243 {built-in method builtins.exec}
        1    0.000    0.000    0.242    0.242 <string>:1(<module>)
        1    0.000    0.000    0.242    0.242 /src/blender/release/scripts/modules/bpy/utils/__init__.py:134(load_scripts)
   126/20    0.002    0.000    0.192    0.010 <frozen importlib._bootstrap>:966(_find_and_load)
   126/20    0.001    0.000    0.190    0.010 <frozen importlib._bootstrap>:939(_find_and_load_unlocked)
   125/20    0.001    0.000    0.186    0.009 <frozen importlib._bootstrap>:659(_load_unlocked)
    97/18    0.001    0.000    0.185    0.010 {built-in method builtins.__import__}
   117/20    0.001    0.000    0.185    0.009 <frozen importlib._bootstrap_external>:656(exec_module)
   213/20    0.000    0.000    0.182    0.009 <frozen importlib._bootstrap>:214(_call_with_frames_removed)
 1231/162    0.001    0.000    0.169    0.001 <frozen importlib._bootstrap>:996(_handle_fromlist)
        1    0.000    0.000    0.143    0.143 /src/blender/release/scripts/modules/bpy/utils/__init__.py:108(modules_from_path)
        4    0.000    0.000    0.143    0.036 /src/blender/release/scripts/modules/bpy/utils/__init__.py:75(_test_import)
        1    0.000    0.000    0.099    0.099 /src/blender/release/scripts/startup/bl_ui/__init__.py:23(<module>)
     1071    0.013    0.000    0.072    0.000 {built-in method builtins.__build_class__}
        1    0.000    0.000    0.057    0.057 /src/blender/release/scripts/modules/addon_utils.py:40(_initialize)
       11    0.000    0.000    0.056    0.005 /src/blender/release/scripts/modules/addon_utils.py:258(enable)
      991    0.007    0.000    0.050    0.000 /src/blender/release/scripts/modules/bpy_types.py:537(__new__)
        4    0.000    0.000    0.041    0.010 /src/blender/release/scripts/modules/bpy/utils/__init__.py:202(test_register)
        4    0.000    0.000    0.041    0.010 /src/blender/release/scripts/modules/bpy/utils/__init__.py:166(register_module_call)
      117    0.001    0.000    0.040    0.000 <frozen importlib._bootstrap_external>:726(get_code)

Linux: perf + hotspot

On Linux, many Python builds have support for the perf tool. The profiling data it saves can be loaded with Hotspot to produce interactive flame graphs.

Hotspot flame graph

On Ubuntu, these tools can be installed with:

$ sudo apt install linux-tools-common hotspot

Run the code & generate the profiling data, and then visualise with Hotspot:

$ env PYTHONPERFSUPPORT=1 perf record -F 63000 -g -o perf.data python yourscript.py
$ hotspot perf.data

The advantage of this approach is that you get an insight into both the C/C++ code and the Python code. The downside is that it's way more noisy than a Python-only profiler.

More info in the Python docs: support for the Linux perf profiler.