Lesson 3 / 25
Measuring Time and Memory
Benchmark operations with timeit and inspect memory with sys.getsizeof and tracemalloc.
Measure, do not guess
Complexity analysis tells you how code scales; measurement tells you how fast it actually is. Use timeit for micro-benchmarks: it runs a statement many times, disables garbage collection during timing by default and reports total time; in a terminal, python -m timeit chooses a sensible number of loops. In Jupyter, use %timeit. For whole programs, use cProfile (python -m cProfile -s cumtime app.py) to find which functions take time, and sampling profilers such as py-spy or Scalene for production-like runs. For memory, sys.getsizeof(obj) gives the size of the container object itself (not the objects it references), so a list of strings is much larger than getsizeof reports; tracemalloc tracks allocations and peak usage, and __slots__, generators, array, and NumPy reduce memory dramatically for large data. Benchmark with realistic data sizes, run several repetitions, compare relative numbers on the same machine, and remember that a faster algorithm usually beats micro-optimisation.
timeit and tracemalloc in practice
Comparing string building and measuring peak memory.
import sys
import timeit
import tracemalloc
words = [f"word{i}" for i in range(10_000)]
def concat_plus():
s = ""
for w in words:
s += w + "," # may copy the growing string repeatedly
return s
def concat_join():
return ",".join(words) + ","
print(timeit.timeit(concat_plus, number=100))
print(timeit.timeit(concat_join, number=100)) # join is typically much faster
print(sys.getsizeof([])) # size of an empty list object
print(sys.getsizeof(words)) # the list's pointer array only
print(sum(sys.getsizeof(w) for w in words)) # the strings themselves
tracemalloc.start()
squares = [n * n for n in range(1_000_000)] # materialises a million ints
current, peak = tracemalloc.get_traced_memory()
print(f"peak {peak / 1e6:.1f} MB")
tracemalloc.stop()
total = sum(n * n for n in range(1_000_000)) # generator: constant memorygetsizeof is shallow
sys.getsizeof(my_list) does not include the elements. To estimate the true footprint of nested data, use tracemalloc or a library such as Pympler, and test with realistic sizes.
Quick check: What does sys.getsizeof(some_list) measure?
- The total memory of the list and all elements
- The number of elements
- Only the list object itself, including its array of references, not the referenced elements
- Disk usage
Answer
Only the list object itself, including its array of references, not the referenced elements — getsizeof is shallow: elements are separate objects.