Module sverchok.utils.profile

Expand source code
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

import cProfile
import pstats
from io import StringIO

from sverchok.utils.sv_logging import sv_logger
from sverchok.utils.context_managers import sv_preferences

# Global cProfile.Profile singleton
_global_profile = None
# Nesting level for @profile decorator
_profile_nesting = 0
# Whether the profiling is enabled by "Start profiling" toggle
is_currently_enabled = False

def get_global_profile():
    """
    Get cProfile.Profile singleton object
    """
    global _global_profile
    if _global_profile is None:
        _global_profile = cProfile.Profile()
    return _global_profile

def is_profiling_enabled(section):
    """
    Check if profiling is enabled in general,
    and if it is enabled for specified section.
    """
    global is_currently_enabled
    if not is_currently_enabled:
        return False
    with sv_preferences() as prefs:
        return prefs.profile_mode == section


def profile(function = None, section = "MANUAL"):
    """
    Decorator for profiling the specific methods.
    It can be used in two ways:

    @profile
    def method(...):
        ...

    or

    @profile(section="SECTION"):
    def method(...):
        ...

    The second form is equivalent to the first with section = "MANUAL".
    Profiling section is a named set of methods which should be profiled.
    Supported values of section are listed in SverchokPreferences.profiling_sections.

    The @profile(section) decorator does profile the method only if all following
    conditions are met:
    * profiling for specified section is enabled in settings (profile_mode option),
    * profiling is currently active.
    """

    def profiling_decorator(func):
        def wrapper(*args, **kwargs):
            if is_profiling_enabled(section):
                global _profile_nesting

                profile = get_global_profile()
                _profile_nesting += 1
                if _profile_nesting == 1:
                    profile.enable()
                result = func(*args, **kwargs)
                _profile_nesting -= 1
                if _profile_nesting == 0:
                    profile.disable()
                return result
            else:
                return func(*args, **kwargs)
            
        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        return wrapper

    if callable(function):
        return profiling_decorator(function)
    else:
        return profiling_decorator

def dump_stats(sort = "tottime", strip_dirs = False, file_path=None):
    """
    Dump profiling statistics to the log.
    """
    profile = get_global_profile()
    if not profile.getstats():
        sv_logger.info("There are no profiling results yet")
        return

    if file_path is None:
        stream = StringIO()
        stats = pstats.Stats(profile, stream=stream)
        if strip_dirs:
            stats.strip_dirs()
        stats = stats.sort_stats(sort)
        stats.print_stats()
        sv_logger.info("Profiling results:\n" + stream.getvalue())
        sv_logger.info("---------------------------")
    else:
        with open(file_path, 'w') as stream:
            stats = pstats.Stats(profile, stream=stream)
            if strip_dirs:
                stats.strip_dirs()
            stats = stats.sort_stats(sort)
            stats.print_stats()
            sv_logger.info("Profiling results are written to %s", file_path)

def save_stats(path):
    """
    Dump profiling statistics to file in cProfile's binary format.
    Such file can be parsed, for example, by gprof2dot utility.
    """
    profile = get_global_profile()
    if not profile.getstats():
        sv_logger.info("There are no profiling results yet")
        return
    stats = pstats.Stats(profile)
    stats.dump_stats(path)
    sv_logger.info("Profiling statistics saved to %s.", path)

def have_gathered_stats():
    global _global_profile
    if _global_profile is None:
        return False
    if _global_profile.getstats():
        return True
    else:
        return False

def reset_stats():
    global _global_profile
    _global_profile = None

Functions

def dump_stats(sort='tottime', strip_dirs=False, file_path=None)

Dump profiling statistics to the log.

Expand source code
def dump_stats(sort = "tottime", strip_dirs = False, file_path=None):
    """
    Dump profiling statistics to the log.
    """
    profile = get_global_profile()
    if not profile.getstats():
        sv_logger.info("There are no profiling results yet")
        return

    if file_path is None:
        stream = StringIO()
        stats = pstats.Stats(profile, stream=stream)
        if strip_dirs:
            stats.strip_dirs()
        stats = stats.sort_stats(sort)
        stats.print_stats()
        sv_logger.info("Profiling results:\n" + stream.getvalue())
        sv_logger.info("---------------------------")
    else:
        with open(file_path, 'w') as stream:
            stats = pstats.Stats(profile, stream=stream)
            if strip_dirs:
                stats.strip_dirs()
            stats = stats.sort_stats(sort)
            stats.print_stats()
            sv_logger.info("Profiling results are written to %s", file_path)
def get_global_profile()

Get cProfile.Profile singleton object

Expand source code
def get_global_profile():
    """
    Get cProfile.Profile singleton object
    """
    global _global_profile
    if _global_profile is None:
        _global_profile = cProfile.Profile()
    return _global_profile
def have_gathered_stats()
Expand source code
def have_gathered_stats():
    global _global_profile
    if _global_profile is None:
        return False
    if _global_profile.getstats():
        return True
    else:
        return False
def is_profiling_enabled(section)

Check if profiling is enabled in general, and if it is enabled for specified section.

Expand source code
def is_profiling_enabled(section):
    """
    Check if profiling is enabled in general,
    and if it is enabled for specified section.
    """
    global is_currently_enabled
    if not is_currently_enabled:
        return False
    with sv_preferences() as prefs:
        return prefs.profile_mode == section
def profile(function=None, section='MANUAL')

Decorator for profiling the specific methods. It can be used in two ways:

@profile def method(…): …

or

@profile(section="SECTION"): def method(…): …

The second form is equivalent to the first with section = "MANUAL". Profiling section is a named set of methods which should be profiled. Supported values of section are listed in SverchokPreferences.profiling_sections.

The @profile(section) decorator does profile the method only if all following conditions are met: * profiling for specified section is enabled in settings (profile_mode option), * profiling is currently active.

Expand source code
def profile(function = None, section = "MANUAL"):
    """
    Decorator for profiling the specific methods.
    It can be used in two ways:

    @profile
    def method(...):
        ...

    or

    @profile(section="SECTION"):
    def method(...):
        ...

    The second form is equivalent to the first with section = "MANUAL".
    Profiling section is a named set of methods which should be profiled.
    Supported values of section are listed in SverchokPreferences.profiling_sections.

    The @profile(section) decorator does profile the method only if all following
    conditions are met:
    * profiling for specified section is enabled in settings (profile_mode option),
    * profiling is currently active.
    """

    def profiling_decorator(func):
        def wrapper(*args, **kwargs):
            if is_profiling_enabled(section):
                global _profile_nesting

                profile = get_global_profile()
                _profile_nesting += 1
                if _profile_nesting == 1:
                    profile.enable()
                result = func(*args, **kwargs)
                _profile_nesting -= 1
                if _profile_nesting == 0:
                    profile.disable()
                return result
            else:
                return func(*args, **kwargs)
            
        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        return wrapper

    if callable(function):
        return profiling_decorator(function)
    else:
        return profiling_decorator
def reset_stats()
Expand source code
def reset_stats():
    global _global_profile
    _global_profile = None
def save_stats(path)

Dump profiling statistics to file in cProfile's binary format. Such file can be parsed, for example, by gprof2dot utility.

Expand source code
def save_stats(path):
    """
    Dump profiling statistics to file in cProfile's binary format.
    Such file can be parsed, for example, by gprof2dot utility.
    """
    profile = get_global_profile()
    if not profile.getstats():
        sv_logger.info("There are no profiling results yet")
        return
    stats = pstats.Stats(profile)
    stats.dump_stats(path)
    sv_logger.info("Profiling statistics saved to %s.", path)