Cornellius Yudha Wijaya
2025-04-03 08:00:00
www.kdnuggets.com

Image by Author | Ideogram
NumPy is a Python package data scientists use to perform many data operations. Many other Python packages are built on top of NumPy, so it’s good to know how to use them.
To improve our NumPy usage experience, we need to know where and why our code performs poorly or at least does not meet our expectations. This article will explore various methods for debugging and profiling the NumPy code to see the performance bottlenecks.
NumPy Code Debugging
Before anything else, we need to ensure that our code executes flawlessly. That is why we need to debug our code before we try to perform any code profiling.
1. Using Assert
The easiest way to perform debugging is to use assert to ensure that our code output is as expected. For example, here is how we can perform the code.
import numpy as np
arr = np.array([1, 2, 3])
assert arr.shape == (3,)
2. Using Python Debugger
We can use Python’s built-in debugger to review the code and assess the process.
import pdb
# Add the code when you need to pause in the middle of the execution.
pdb.set_trace()
3. Using Try and Except Block
Using the Try and Except block is also an excellent way to debug and ensure we know what is going wrong.
try:
a = np.array([1, 2, 3])
print(a[5])
except IndexError as e:
print("Caught an Error:", e)
Output:
Caught an Error: index 5 is out of bounds for axis 0 with size 3
NumPy Code Profiling
When we have finished the debugging process, we will profile the NumPy code execution to understand the performance bottleneck in our code.
1. Profiling with Time
The simplest way to profile the performance is to understand the execution time manually. For example, we can use the following code to learn more about it.
import time
start_time = time.time()
np.dot(np.random.rand(1000, 1000), np.random.rand(1000, 1000))
end_time = time.time()
print(f"Execution Time: {end_time - start_time} seconds")
Output:
Execution Time: 0.03861522674560547 seconds
You can try various code combinations to see if the code execution becomes faster or slower.
2. Profiling with cProfile
We can profile them in more detail with the cProfile package. Let’s see how it works.
import cProfile
def my_numpy_operation():
np.dot(np.random.rand(1000, 1000), np.random.rand(1000, 1000))
cProfile.run('my_numpy_operation()')
Output:
7 function calls in 0.031 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.016 0.016 0.031 0.031 :3(my_numpy_operation)
1 0.000 0.000 0.031 0.031 :1()
1 0.000 0.000 0.000 0.000 multiarray.py:741(dot)
1 0.000 0.000 0.031 0.031 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.015 0.008 0.015 0.008 {method 'rand' of 'numpy.random.mtrand.RandomState' objects}
As you can see, cProfile works by dissecting the whole process of our NumPy code and providing details. The information such as how many times the process is called, the time it took, and what method was called.
3. Profiling with line_profiler
We can also use the package line_profiler to get the details for each line in our NumPy code. First, we need to install it.
pip install line_profiler
After installing the package, we would use the following magic commands if you run it in the Jupyter Notebook.
Let’s prepare the NumPy code that we want to profile.
import numpy as np
def matrix_multiplication(n):
a = np.random.rand(n, n)
b = np.random.rand(n, n)
result = np.dot(a, b)
return result
Then, use the following magic command to see how the code works under the hood.
%lprun -f matrix_multiplication matrix_multiplication(500)
Output:
Timer unit: 1e-09 s
Total time: 0.0069203 s
File:
Function: matrix_multiplication at line 3
Line # Hits Time Per Hit % Time Line Contents
==============================================================
3 def matrix_multiplication(n):
4 1 2165161.0 2e+06 31.3 a = np.random.rand(n, n)
5 1 1824265.0 2e+06 26.4 b = np.random.rand(n, n)
6 1 2930093.0 3e+06 42.3 result = np.dot(a, b)
7 1 780.0 780.0 0.0 return result
The result will be similar to the above report. You can see the performance details and where the performance bottlenecks were.
4. Profiling with memory_profiling
Lastly, we can also see the performance bottleneck from the memory standpoint. To do that, we can use the memory_profiling package.
pip install memory_profiler
We will use the magic command below to initiate memory profiling in Jupyter Notebook.
%load_ext memory_profiler
Then, let’s prepare the NumPy code we want to execute and use the magic command below to get the memory information.
def create_large_array():
a = [i for i in range(10**6)]
return sum(a)
%memit create_large_array()
Output:
peak memory: 5793.52 MiB, increment: 0.01 MiB
From the code above, we get the total memory information and the incrementation memory usage for the whole process. This information is vital, especially if your memory is limited.
Conclusion
Debugging and Profiling NumPy Code is important to understanding where our performance bottlenecks were. In this article, we explore various methods to pinpoint the problems. We can gain performance information From the simple assert Python command to a library such as memory_profiling.
I hope this has helped!
Cornellius Yudha Wijaya is a data science assistant manager and data writer. While working full-time at Allianz Indonesia, he loves to share Python and data tips via social media and writing media. Cornellius writes on a variety of AI and machine learning topics.
Transform your cleaning routine with the Shark AI Ultra Voice Control Robot Vacuum! This high-tech marvel boasts over 32,487 ratings, an impressive 4.2 out of 5 stars, and has been purchased over 900 times in the past month. Perfect for keeping your home spotless with minimal effort, this vacuum is now available for the unbeatable price of $349.99!
Don’t miss out on this limited-time offer. Order now and let Shark AI do the work for you!
Help Power Techcratic’s Future – Scan To Support
If Techcratic’s content and insights have helped you, consider giving back by supporting the platform with crypto. Every contribution makes a difference, whether it’s for high-quality content, server maintenance, or future updates. Techcratic is constantly evolving, and your support helps drive that progress.
As a solo operator who wears all the hats, creating content, managing the tech, and running the site, your support allows me to stay focused on delivering valuable resources. Your support keeps everything running smoothly and enables me to continue creating the content you love. I’m deeply grateful for your support, it truly means the world to me! Thank you!
BITCOIN bc1qlszw7elx2qahjwvaryh0tkgg8y68enw30gpvge Scan the QR code with your crypto wallet app |
DOGECOIN D64GwvvYQxFXYyan3oQCrmWfidf6T3JpBA Scan the QR code with your crypto wallet app |
ETHEREUM 0xe9BC980DF3d985730dA827996B43E4A62CCBAA7a Scan the QR code with your crypto wallet app |
Please read the Privacy and Security Disclaimer on how Techcratic handles your support.
Disclaimer: As an Amazon Associate, Techcratic may earn from qualifying purchases.