Skip to content

Profiling & Performance

Before optimising anything, you need to measure. The same rule applies in both TypeScript and Python: do not guess where the bottleneck is — profile first, optimise second, measure again.

Node.js toolPython equivalent
console.time / console.timeEndtimeit.timeit()
performance.now()time.perf_counter()
node --prof + node --prof-processcProfile + pstats
Chrome DevTools flame graphsnakeviz (pip install snakeviz)
clinic.js / 0xpy-spy (external, sampling profiler)

timeit runs a snippet thousands of times and reports the total time. It is the Python equivalent of console.time but much more rigorous — it disables garbage collection during the run to reduce noise.

TypeScript
// TypeScript — manual timing
console.time("list comp");
for (let i = 0; i < 10_000; i++) {
Array.from({ length: 100 }, (_, i) => i ** 2);
}
console.timeEnd("list comp");
Python
import timeit
# timeit runs the snippet 10,000 times; returns total seconds
t = timeit.timeit(
'[x**2 for x in range(100)]',
number=10_000
)
print(f"List comprehension: {t:.4f}s")
# Compare two approaches
t2 = timeit.timeit(
'list(map(lambda x: x**2, range(100)))',
number=10_000
)
print(f"map(): {t2:.4f}s")
import timeit
# Compare list comprehension vs map vs generator-to-list
approaches = {
"list comp": '[x**2 for x in range(100)]',
"map+list ": 'list(map(lambda x: x**2, range(100)))',
"for loop ": 'r=[]; [r.append(x**2) for x in range(100)]',
}
results = {}
for name, stmt in approaches.items():
t = timeit.timeit(stmt, number=5000)
results[name] = t
print(f" {name}: {t:.4f}s")
fastest = min(results, key=results.get)
print(f"\nFastest: {fastest}")

cProfile is Python’s built-in deterministic profiler. It records every function call, how many times each was called, and how long each took. It is the equivalent of node --prof but ships with Python and needs no extra tools to read.

TypeScript
// Node.js — file-based profiling
// node --prof server.js
// node --prof-process isolate-*.log > profile.txt
Python
import cProfile
import pstats
import io
def slow_function():
total = 0
for i in range(1000):
total += sum(range(i))
return total
# Profile a specific function
pr = cProfile.Profile()
pr.enable()
slow_function()
pr.disable()
# Print top 5 by cumulative time
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
ps.print_stats(5)
print(s.getvalue())
import cProfile
import pstats
import io
def inner(n: int) -> int:
return sum(range(n))
def middle(n: int) -> int:
return sum(inner(i) for i in range(n))
def outer(n: int) -> int:
return middle(n) + inner(n)
pr = cProfile.Profile()
pr.enable()
result = outer(200)
pr.disable()
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats("cumulative")
ps.print_stats(8)
print(f"Result: {result}")
print()
print(s.getvalue())
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.004 0.004 script.py:4(slow_function)
1000 0.004 0.000 0.004 0.000 {built-in method builtins.sum}
ColumnMeaning
ncallsNumber of times the function was called
tottimeTime spent in this function (excluding callees)
cumtimeCumulative time (including all sub-calls)
percallAverage time per call

Sort strategies:

  • sort_stats('cumulative') — find the slowest end-to-end call chains (start here)
  • sort_stats('tottime') — find functions doing the most work themselves
  • sort_stats('ncalls') — find functions called unexpectedly often

Workflow: profile → identify → fix → measure

Section titled “Workflow: profile → identify → fix → measure”
# 1. Profile
import cProfile
cProfile.run('my_function()', 'profile.stats')
# 2. Inspect interactively
import pstats
p = pstats.Stats('profile.stats')
p.sort_stats('cumulative').print_stats(10)
# 3. Visualise (install once)
# pip install snakeviz
# snakeviz profile.stats # opens browser flame graph
What does `timeit.timeit(stmt, number=10_000)` return?
In cProfile's pstats output, what does `cumtime` represent?
Which pstats sort key finds the functions consuming the most total time themselves (not sub-calls)?
Why does timeit disable garbage collection during its runs?