Benchmark

As programs increase in size, they have the risk of getting slow as new features are added and extra layers of complexity to the main features. Benchmarking is an approach that helps developers use profiling metrics and their code intuition to optimize programs further. This module uses cProfile to compare the performance of two functions with each other.

import cProfile
import io
import pstats
import time

# Module-level constants
_SLEEP_DURATION = .001


def finish_slower():
    """Finish slower by sleeping more."""
    for _ in range(20):
        time.sleep(_SLEEP_DURATION)


def finish_faster():
    """Finish faster by sleeping less."""
    for _ in range(10):
        time.sleep(_SLEEP_DURATION)


def main():
    # Create a profile instance
    profile = cProfile.Profile()

    profile.enable()

    for _ in range(2):
        finish_slower()
        finish_faster()

    profile.disable()

    # Sort statistics by cumulative time spent for each function call.
    # There are other ways to sort the stats by, but this is the most
    # common way of doing so. For more info, please consult Python docs:
    # https://docs.python.org/3/library/profile.html
    buffer = io.StringIO()
    ps = pstats.Stats(profile, stream=buffer).sort_stats("cumulative")

    # Notice how many times each function was called. In this case, the main
    # bottleneck for `finish_slower` and `finish_faster` is `time.sleep`
    # which occurred 60 times. By reading the code and the statistics, we
    # can infer that 40 occurrences came from `finish_slower` and 20 came
    # from `finish_faster`. It is clear why the latter function runs faster
    # in this case, but identifying insights like this are not simple in
    # large projects. Consider profiling in isolation when analyzing complex
    # classes and functions
    ps.print_stats()
    time_sleep_called = any("60" in line and "time.sleep" in line
                            for line in buffer.getvalue().split("\n"))
    assert time_sleep_called is True


if __name__ == "__main__":
    main()