- Reactive Programming
- Event-Driven Programming
- Processes Intercommunication
- Multiprocessing
- Pool of Processes
- Pool of Threads
- Benchmarking & Profiling
- Debugging Thread Applications
- Testing Thread Applications
- Threads Intercommunication
- Synchronizing Threads
- Implementation of Threads
- Threads
- System & Memory Architecture
- Concurrency vs Parallelism
- Introduction
- Home
Concurrency in Python Resources
Selected Reading
- Who is Who
- Computer Glossary
- HR Interview Questions
- Effective Resume Writing
- Questions and Answers
- UPSC IAS Exams Notes
Benchmarking and Profipng
In this chapter, we will learn how benchmarking and profipng help in addressing performance issues.
Suppose we had written a code and it is giving the desired result too but what if we want to run this code a bit faster because the needs have changed. In this case, we need to find out what parts of our code are slowing down the entire program. In this case, benchmarking and profipng can be useful.
What is Benchmarking?
Benchmarking aims at evaluating something by comparison with a standard. However, the question that arises here is that what would be the benchmarking and why we need it in case of software programming. Benchmarking the code means how fast the code is executing and where the bottleneck is. One major reason for benchmarking is that it optimizes the code.
How does benchmarking work?
If we talk about the working of benchmarking, we need to start by benchmarking the whole program as one current state then we can combine micro benchmarks and then decompose a program into smaller programs. In order to find the bottlenecks within our program and optimize it. In other words, we can understand it as breaking the big and hard problem into series of smaller and a bit easier problems for optimizing them.
Python module for benchmarking
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.
Example
In the following Python script, we are importing the timeit module, which further measures the time taken to execute two functions – functionA and functionB −
import timeit import time def functionA(): print("Function A starts the execution:") print("Function A completes the execution:") def functionB(): print("Function B starts the execution") print("Function B completes the execution") start_time = timeit.default_timer() functionA() print(timeit.default_timer() - start_time) start_time = timeit.default_timer() functionB() print(timeit.default_timer() - start_time)
After running the above script, we will get the execution time of both the functions as shown below.
Output
Function A starts the execution: Function A completes the execution: 0.0014599495514175942 Function B starts the execution Function B completes the execution 0.0017024724827479076
Writing our own timer using the decorator function
In Python, we can create our own timer, which will act just pke the timeit module. It can be done with the help of the decorator function. Following is an example of the custom timer −
import random import time def timer_func(func): def function_timer(*args, **kwargs): start = time.time() value = func(*args, **kwargs) end = time.time() runtime = end - start msg = "{func} took {time} seconds to complete its execution." print(msg.format(func = func.__name__,time = runtime)) return value return function_timer @timer_func def Myfunction(): for x in range(5): sleep_time = random.choice(range(1,3)) time.sleep(sleep_time) if __name__ == __main__ : Myfunction()
The above python script helps in importing random time modules. We have created the timer_func() decorator function. This has the function_timer() function inside it. Now, the nested function will grab the time before calpng the passed in function. Then it waits for the function to return and grabs the end time. In this way, we can finally make python script print the execution time. The script will generate the output as shown below.
Output
Myfunction took 8.000457763671875 seconds to complete its execution.
What is profipng?
Sometimes the programmer wants to measure some attributes pke the use of memory, time complexity or usage of particular instructions about the programs to measure the real capabipty of that program. Such kind of measuring about program is called profipng. Profipng uses dynamic program analysis to do such measuring.
In the subsequent sections, we will learn about the different Python Modules for Profipng.
cProfile – the inbuilt module
cProfile is a Python built-in module for profipng. The module is a C-extension with reasonable overhead that makes it suitable for profipng long-running programs. After running it, it logs all the functions and execution times. It is very powerful but sometimes a bit difficult to interpret and act on. In the following example, we are using cProfile on the code below −
Example
def increment_global(): global x x += 1 def taskofThread(lock): for _ in range(50000): lock.acquire() increment_global() lock.release() def main(): global x x = 0 lock = threading.Lock() t1 = threading.Thread(target=taskofThread, args=(lock,)) t2 = threading.Thread(target= taskofThread, args=(lock,)) t1.start() t2.start() t1.join() t2.join() if __name__ == "__main__": for i in range(5): main() print("x = {1} after Iteration {0}".format(i,x))
The above code is saved in the thread_increment.py file. Now, execute the code with cProfile on the command pne as follows −
(base) D:ProgramData>python -m cProfile thread_increment.py x = 100000 after Iteration 0 x = 100000 after Iteration 1 x = 100000 after Iteration 2 x = 100000 after Iteration 3 x = 100000 after Iteration 4 3577 function calls (3522 primitive calls) in 1.688 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:pneno(function) 5 0.000 0.000 0.000 0.000 <frozen importpb._bootstrap>:103(release) 5 0.000 0.000 0.000 0.000 <frozen importpb._bootstrap>:143(__init__) 5 0.000 0.000 0.000 0.000 <frozen importpb._bootstrap>:147(__enter__) … … … …
From the above output, it is clear that cProfile prints out all the 3577 functions called, with the time spent in each and the number of times they have been called. Followings are the columns we got in output −
ncalls − It is the number of calls made.
tottime − It is the total time spent in the given function.
percall − It refers to the quotient of tottime spanided by ncalls.
cumtime − It is the cumulative time spent in this and all subfunctions. It is even accurate for recursive functions.
percall − It is the quotient of cumtime spanided by primitive calls.
filename:pneno(function) − It basically provides the respective data of each function.