How to Identify CPU and Memory Inefficiencies

Mar 12 · 4 min read

To take your code to the next level, you need to ensure that your solutions are as efficient as possible! But how do we identify the bottlenecks in speed or memory usage? We profile!

CPU Usage and Timing

With a Custom Decorator

Decorators are functions that take other functions as arguments — an example of a higher-order function. In short, they add additional functionality (pun intended) without modifying the original function. In this case, our fn_timer decorator takes in a function and adds a timer by storing a start time, running the function, then storing an end time and returning a value of elapsed time!

`import timefrom functools import wrapsdef fn_timer(function):    @wraps(function)    def function_timer(*args, **kwargs):        start_time = time.time()        result = function(*args, **kwargs)        end_time = time.time()        print(f"Runtime of {function.__name__} is {end_time -                       start_time:.04} seconds.")        return result    return function_timer@fn_timerdef sort_list(n):    return sorted([i for i in range(n)])if __name__ == "__main__":    random_sort(100000000)`

And we get the following print statement.

`Runtime of sort_list is 6.044 seconds.`

But what if we don’t just care about overall time of the function but what specific calls are taking the most time?

With cProfile

cProfile is included in the Python standard library, so no importing needed!

`python -m cProfile -s cumulative functions.py`

And here’s the output:

So we see a breakdown of our script to see how long and how many times each function/method is being called. For example, on line 16 our sort_list function is called. The list comprehension creating our list of numbers to sort takes 3.924 seconds and was called 1 time. And the sorted() method takes 2.058 seconds and was called 1 time. As you can see, because Python is dynamically typed, we lose some efficiency has Python has to decide how to handle our variables.

Kernel Profiler

Using kernprof allows us to analyze our function line by line using the -l flag. Our function above is actually a one-liner so I rewrote it to be very inefficient and more lines so we can better visualize our results.

`kernprof -l -v functions.py`

Here’s a new, very bad function doing the same thing as above, but much slower. Also note that just running the profiler adds an overhead to the runtime.

`def sort_list(n):    new_list = []    for i in range(n):        new_list.append(i)    sorted_list = sorted(new_list)    return sorted_list`

This took about 3 minutes to run on my MacBook Pro with 16 GB RAM. You can see that our “for loop” and “append” method were each called 100,000,000 times!

Memory Usage

Just like line profiling, we can use the “@profile” decorator on our target function to analyze its memory. This has an even greater time overhead than timing so beware of already long-running functions.

Let’s install the package:

`conda install memory_profiler`

And run with:

`python -m memory_profiler functions.py`

And we get our output! I actually had to decrease the size of our lists because the memory profiler was taking +45 minutes to run.

You can see that our list creation is responsible for 2.836 and 3.031 MiB, respectively. And the sorted list only increased our memory usage by 0.766 MiB. We cannot say that the new_list is 3.031 MiB in size, just that our memory usage increased by that amount throughout its creation. So now we know where to start improving our code!

Conclusion

I hope this has been helpful and will give you a jumpstart on how to identify bottlenecks in your code through profiling for CPU and memory usage. There are often small improvements you can make to speed up your code and these really add up especially in long-running processes and in production environments.

Connect

I’m always looking to connect and explore other projects! You can follow me on GitHub or LinkedIn, and check out my other stories on Medium. I also have a Twitter!

CodeX

Everything connected with Code & Tech!

Written by

Aren Carpenter

Data Scientist. Exploring the intersection between AI and Healthcare/Oncology. Flatiron alum.

CodeX

Everything connected with Code & Tech!

Written by

Aren Carpenter

Data Scientist. Exploring the intersection between AI and Healthcare/Oncology. Flatiron alum.

CodeX

Everything connected with Code & Tech!

Twisk — Golang RPC starter kit

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app