Have a look at timeit, the python profiler and pycallgraph. Also make sure to have a look at the comment below by nikicc mentioning "SnakeViz". It gives you yet another visualisation of profiling data which can be helpful.

timeit

def test():
    """Stupid test function"""
    lst = []
    for i in range(100):
        lst.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

    # For Python>=3.5 one can also write:
    print(timeit.timeit("test()", globals=locals()))

Essentially, you can pass it python code as a string parameter, and it will run in the specified amount of times and prints the execution time. The important bits from the docs:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None) Create a Timer instance with the given statement, setup code and timer function and run its timeit method with number executions. The optional globals argument specifies a namespace in which to execute the code.

... and:

Timer.timeit(number=1000000) Time number executions of the main statement. This executes the setup statement once, and then returns the time it takes to execute the main statement a number of times, measured in seconds as a float. The argument is the number of times through the loop, defaulting to one million. The main statement, the setup statement and the timer function to be used are passed to the constructor.

Note: By default, timeit temporarily turns off garbage collection during the timing. The advantage of this approach is that it makes independent timings more comparable. This disadvantage is that GC may be an important component of the performance of the function being measured. If so, GC can be re-enabled as the first statement in the setup string. For example:

timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()

Profiling

Profiling will give you a much more detailed idea about what's going on. Here's the "instant example" from the official docs:

import cProfile
import re
cProfile.run('re.compile("foo|bar")')

Which will give you:

      197 function calls (192 primitive calls) in 0.002 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.001    0.001 <string>:1(<module>)
     1    0.000    0.000    0.001    0.001 re.py:212(compile)
     1    0.000    0.000    0.001    0.001 re.py:268(_compile)
     1    0.000    0.000    0.000    0.000 sre_compile.py:172(_compile_charset)
     1    0.000    0.000    0.000    0.000 sre_compile.py:201(_optimize_charset)
     4    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
   3/1    0.000    0.000    0.000    0.000 sre_compile.py:33(_compile)

Both of these modules should give you an idea about where to look for bottlenecks.

Also, to get to grips with the output of profile, have a look at this post

pycallgraph

NOTE pycallgraph has been officially abandoned since Feb. 2018. As of Dec. 2020 it was still working on Python 3.6 though. As long as there are no core changes in how python exposes the profiling API it should remain a helpful tool though.

This module uses graphviz to create callgraphs like the following:

You can easily see which paths used up the most time by colour. You can either create them using the pycallgraph API, or using a packaged script:

pycallgraph graphviz -- ./mypythonscript.py

The overhead is quite considerable though. So for already long-running processes, creating the graph can take some time.

Answer from exhuma on Stack Overflow
Top answer
1 of 15
164

Have a look at timeit, the python profiler and pycallgraph. Also make sure to have a look at the comment below by nikicc mentioning "SnakeViz". It gives you yet another visualisation of profiling data which can be helpful.

timeit

def test():
    """Stupid test function"""
    lst = []
    for i in range(100):
        lst.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

    # For Python>=3.5 one can also write:
    print(timeit.timeit("test()", globals=locals()))

Essentially, you can pass it python code as a string parameter, and it will run in the specified amount of times and prints the execution time. The important bits from the docs:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None) Create a Timer instance with the given statement, setup code and timer function and run its timeit method with number executions. The optional globals argument specifies a namespace in which to execute the code.

... and:

Timer.timeit(number=1000000) Time number executions of the main statement. This executes the setup statement once, and then returns the time it takes to execute the main statement a number of times, measured in seconds as a float. The argument is the number of times through the loop, defaulting to one million. The main statement, the setup statement and the timer function to be used are passed to the constructor.

Note: By default, timeit temporarily turns off garbage collection during the timing. The advantage of this approach is that it makes independent timings more comparable. This disadvantage is that GC may be an important component of the performance of the function being measured. If so, GC can be re-enabled as the first statement in the setup string. For example:

timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()

Profiling

Profiling will give you a much more detailed idea about what's going on. Here's the "instant example" from the official docs:

import cProfile
import re
cProfile.run('re.compile("foo|bar")')

Which will give you:

      197 function calls (192 primitive calls) in 0.002 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.001    0.001 <string>:1(<module>)
     1    0.000    0.000    0.001    0.001 re.py:212(compile)
     1    0.000    0.000    0.001    0.001 re.py:268(_compile)
     1    0.000    0.000    0.000    0.000 sre_compile.py:172(_compile_charset)
     1    0.000    0.000    0.000    0.000 sre_compile.py:201(_optimize_charset)
     4    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
   3/1    0.000    0.000    0.000    0.000 sre_compile.py:33(_compile)

Both of these modules should give you an idea about where to look for bottlenecks.

Also, to get to grips with the output of profile, have a look at this post

pycallgraph

NOTE pycallgraph has been officially abandoned since Feb. 2018. As of Dec. 2020 it was still working on Python 3.6 though. As long as there are no core changes in how python exposes the profiling API it should remain a helpful tool though.

This module uses graphviz to create callgraphs like the following:

You can easily see which paths used up the most time by colour. You can either create them using the pycallgraph API, or using a packaged script:

pycallgraph graphviz -- ./mypythonscript.py

The overhead is quite considerable though. So for already long-running processes, creating the graph can take some time.

2 of 15
47

I use a simple decorator to time the func

import time

def st_time(func):
    """
        st decorator to calculate the total time of a func
    """

    def st_func(*args, **keyArgs):
        t1 = time.time()
        r = func(*args, **keyArgs)
        t2 = time.time()
        print("Function=%s, Time=%s" % (func.__name__, t2 - t1))
        return r

    return st_func
🌐
Switowski
switowski.com › blog › how-to-benchmark-python-code
How to Benchmark (Python) Code - Sebastian Witowski
You can write python -m timeit your_code(), and Python will print out how long it took to run whatever your_code() does. I like to put the code I want to benchmark inside a function for more clarity, ...
🌐
Super Fast Python
superfastpython.com › home › tutorials › 4 ways to benchmark python code
4 Ways to Benchmark Python Code - Super Fast Python
October 4, 2023 - We can benchmark Python code using the time module. The time.perf_counter() function will return a value from a high-performance counter.
🌐
PyPI
pypi.org › project › benchmark-functions
benchmark-functions
JavaScript is disabled in your browser · Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
TutorialsPoint
tutorialspoint.com › concurrency_in_python › concurrency_in_python_benchmarking_and_profiling.htm
Benchmarking & Profiling
In Python, we have a by default module for benchmarking which is called timeit. With the help of the timeit module, we can measure the performance of small bit of Python code within our main program.
🌐
PyPI
pypi.org › project › pytest-benchmark
pytest-benchmark · PyPI
A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer.
      » pip install pytest-benchmark
    
Published   Nov 09, 2025
Version   5.2.3
🌐
Eli Bendersky
eli.thegreenplace.net › 2025 › benchmarking-utility-for-python
Benchmarking utility for Python - Eli Bendersky's website
February 18, 2025 - Over time Python added some niceties to the timeit function, such as the globals parameter - in the past it used to be more cumbersome! This invocation simply runs the benchmarked code (still provided as a string, though there's also an option to pass a lambda, with the caveat that the function ...
🌐
arXiv
arxiv.org › html › 2406.16195v1
A Python Benchmark Functions Framework for Numerical Optimisation Problems
June 23, 2024 - This work proposes a framework of benchmark functions designed to facilitate the creation of test cases for numerical optimisation techniques. The framework, written in Python 3, is designed to be easy to install, use, and expand. The collection includes some of the most used multi-modal continuous ...
Find elsewhere
🌐
Super Fast Python
superfastpython.com › home › tutorials › how to benchmark a python function
How to Benchmark a Python Function - Super Fast Python
October 30, 2023 - You can benchmark a Python function using the time.perf_counter() function or the timeit module. Either method can be used to estimate the execution time of a Python code. Benchmarking is an important step when improving the execution speed ...
🌐
arXiv
arxiv.org › abs › 2406.16195
[2406.16195] A Python Benchmark Functions Framework for Numerical Optimisation Problems
June 23, 2024 - This work proposes a framework of benchmark functions designed to facilitate the creation of test cases for numerical optimisation techniques. The framework, written in Python 3, is designed to be...
🌐
Towards Data Science
towardsdatascience.com › home › latest › benchmarking python code with timeit
Benchmarking Python code with timeit | Towards Data Science
March 5, 2025 - The most popular tool for time benchmarking of Python code, the built-in timeit module offers more than most Pythonistas know
🌐
Towards Data Science
towardsdatascience.com › home › latest › benchmarking python functions the easy way: perftester
Benchmarking Python Functions the Easy Way: perftester | Towards Data Science
November 26, 2024 - The above-mentioned article showed that the timeit module is easy to use. While it’s true, perftester can be even easier. Its API enables you to write concise and clear benchmarks of Python functions and other callables.
🌐
Mouse Vs Python
blog.pythonlibrary.org › home › python 101: an intro to benchmarking your code
Python 101: An Intro to Benchmarking your code - Mouse Vs Python
January 31, 2020 - What does it mean to benchmark ones code? The main idea behind benchmarking or profiling is to figure out how fast your code executes and where the
🌐
GitHub
github.com › python › pyperformance
GitHub - python/pyperformance: Python Performance Benchmark Suite · GitHub
The pyperformance project is intended to be an authoritative source of benchmarks for all Python implementations.
Starred by 1K users
Forked by 202 users
Languages   Python 84.0% | HTML 14.4% | Shell 1.6%
🌐
Peterbe.com
peterbe.com › plog › how-to-do-performance-micro-benchmarks-in-python
How to do performance micro benchmarks in Python - Peterbe.com
June 24, 2017 - Suppose that you have a function and you wonder, "Can I make this faster?" Well, you might already have thought that and you might already have a theory. Or two. Or three. Your theory might be sound and likely to be right, but before you go anywhere with it you need to benchmark it first. Here are some tips and scaffolding for doing Python ...
🌐
Perfpy
perfpy.com
perfpy: Benchmark Python Snippets Online
We cannot provide a description for this page right now
🌐
Reddit
reddit.com › r/python › how to benchmark python code
r/Python on Reddit: How to Benchmark Python Code
November 25, 2022 - Go Benchmark performance scaling with number of cores. ... I actually used Python practically the first time today!
🌐
Bencher
bencher.dev › learn › benchmarking › python › pytest-benchmark
How to benchmark Python code with pytest-benchmark | Bencher - Continuous Benchmarking
November 3, 2024 - Both our main execution and the test_game function can stay exactly the same. ... ======================================================= test session starts ======================================================== platform darwin -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0 · benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
🌐
GitHub
github.com › tonybaloney › rich-bench
GitHub - tonybaloney/rich-bench: A little benchmarking tool for Python · GitHub
richbench encourages you to write benchmarks inside functions to properly simulate the closures and scope of production code. Requires Python 3.6+ and can be installed using pip:
Starred by 207 users
Forked by 7 users
Languages   Python
🌐
PyPI
pypi.org › project › benchmark
benchmark · PyPI
import benchmark import math class Benchmark_Sqrt(benchmark.Benchmark): each = 100 # allows for differing number of runs def setUp(self): # Only using setUp in order to subclass later # Can also specify tearDown, eachSetUp, and eachTearDown self.size = 25000 def test_pow_operator(self): for i in xrange(self.size): z = i**.5 def test_pow_function(self): for i in xrange(self.size): z = pow(i, .5) def test_sqrt_function(self): for i in xrange(self.size): z = math.sqrt(i) class Benchmark_Sqrt2(Benchmark_Sqrt): # Subclass the previous benchmark to change input using # self.setUp() label = "Benchmar
      » pip install benchmark
    
Published   Apr 17, 2012
Version   0.1.5