24. Performance Optimization in Python

Performance optimization improves:

  • Speed
  • Memory usage
  • Efficiency

Optimization is important for:

  • Large applications
  • Data processing
  • APIs
  • Machine learning
  • Automation scripts

Why Performance Optimization Matters

Benefits:

  • Faster execution
  • Lower memory consumption
  • Better scalability
  • Improved user experience

Performance Optimization Areas

Main areas:

  • Profiling
  • Memory optimization
  • Efficient coding practices

Profiling in Python

Profiling helps identify:

  • Slow functions
  • CPU usage
  • Performance bottlenecks

Using time Module

Simple way to measure execution time.

import time

start = time.time()

total = 0

for i in range(1000000):
total += i

end = time.time()

print("Execution Time:", end - start)

Using timeit

timeit measures small code snippets accurately.

import timeit

code = """
sum(range(1000))
"""

execution_time = timeit.timeit(
code,
number=1000
)

print(execution_time)

cProfile Module

cProfile is built-in profiler.


Example

import cProfile

def calculate():

total = 0

for i in range(100000):

total += i

return total

cProfile.run("calculate()")

Sample Output

function calls
ordered by cumulative time

Line Profiler

Used for line-by-line profiling.

Install:

pip install line_profiler

Example

@profile
def test():

total = 0

for i in range(10000):

total += i

return total

Run:

kernprof -l script.py
python -m line_profiler script.py.lprof

Memory Optimization

Memory optimization reduces RAM usage.

Important for:

  • Large datasets
  • Long-running applications
  • High-performance systems

Using sys.getsizeof

Check object memory size.

import sys

numbers = [1, 2, 3]

print(sys.getsizeof(numbers))

Generator vs List

Generators use less memory.


List Example

numbers = [x for x in range(1000000)]

Consumes large memory.


Generator Example

numbers = (x for x in range(1000000))

Consumes less memory.


Using Generators

def generate_numbers():

for i in range(5):
yield i

for num in generate_numbers():
print(num)

Delete Unused Variables

data = [1] * 1000000

del data

Use Appropriate Data Structures

Choose correct structure for performance.

StructureBest Use
ListOrdered data
SetFast lookup
TupleImmutable data
DictionaryKey-value storage

Fast Membership with Sets

numbers = {1, 2, 3, 4}

print(3 in numbers)

Efficient Coding Practices

Writing optimized code improves performance.


Avoid Repeated Computation


Bad Example

for i in range(1000):

result = sum(range(100))

Better Example

result = sum(range(100))

for i in range(1000):

print(result)

Use List Comprehensions

Faster than loops.


Traditional Loop

squares = []

for i in range(10):

squares.append(i * i)

List Comprehension

squares = [i * i for i in range(10)]

String Joining Optimization


Slow Method

text = ""

for word in ["Python", "is", "fast"]:

text += word

Faster Method

words = ["Python", "is", "fast"]

text = " ".join(words)

Use Built-in Functions

Built-in functions are optimized in C.

numbers = [1, 2, 3]

print(sum(numbers))
print(max(numbers))

Avoid Global Variables

Global variables are slower.


Better Example

def calculate():

local_var = 10

return local_var

Use enumerate

Cleaner and efficient iteration.

names = ["A", "B", "C"]

for index, name in enumerate(names):

print(index, name)

Use zip

Efficient iteration over multiple lists.

names = ["A", "B"]
scores = [90, 80]

for name, score in zip(names, scores):

print(name, score)

Memoization

Caches repeated computations.


Using lru_cache

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):

if n < 2:
return n

return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))

NumPy for Performance

NumPy operations are faster than Python loops.


Python Loop

numbers = [1, 2, 3]

result = []

for n in numbers:

result.append(n * 2)

NumPy Version

import numpy as np

arr = np.array([1, 2, 3])

print(arr * 2)

Multithreading and Multiprocessing

Use concurrency for faster execution.

  • Threads → I/O tasks
  • Processes → CPU tasks

Example Using Multiprocessing

from multiprocessing import Pool

def square(n):
return n * n

with Pool() as pool:

results = pool.map(
square,
[1, 2, 3, 4]
)

print(results)

Garbage Collection

Python automatically manages memory.


Manual Garbage Collection

import gc

gc.collect()

Practical Example

Compare List vs Generator Memory

import sys

list_data = [x for x in range(100000)]

gen_data = (x for x in range(100000))

print(
"List:",
sys.getsizeof(list_data)
)

print(
"Generator:",
sys.getsizeof(gen_data)
)

Optimization Best Practices

✅ Profile before optimizing
✅ Use generators for large data
✅ Avoid unnecessary loops
✅ Use built-in functions
✅ Use efficient data structures
✅ Cache repeated computations


Common Optimization Mistakes

❌ Premature optimization
❌ Ignoring readability
❌ Excessive memory allocation
❌ Using wrong data structures


Summary

In this chapter you learned:

✅ Profiling techniques
✅ Memory optimization
✅ Efficient coding practices
✅ Generators and caching
✅ Performance improvement methods