Python Module 17
Performance, profiling and debugging in depth interview questions
Interview questions on performance, profiling and debugging in depth: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
Why does timeit report the best of several repeats instead of their average?
Answered in the lesson Measuring speed correctly with timeit
-
Fresher to mid
What is the difference between tottime and cumtime in a cProfile report?
Answered in the lesson Profiling CPU time: cProfile and pstats
-
Fresher to mid
Why does sys.getsizeof() report much less memory for a list of objects than the list really uses?
Answered in the lesson Finding memory problems with tracemalloc
-
Fresher to mid
Why is sum(numbers) faster than adding the numbers up in a for loop?
Answered in the lesson Making Python code faster: proven techniques
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
A teammate says their new parser is 40 % faster: they ran each version once on their laptop and timed it with time.time(). What do you need before you believe the number, and how would you measure the change yourself?
- What it tests
- Knowing the sources of benchmark noise, choosing the clock and the method, checking that both versions do the same work, and reporting a result with its context.
- First step
- Ask what one run of each version contained: start-up, imports and warming caches, timed with a clock that follows the system clock. Then time both versions in one session, on the same input, with many repeats in turns, after checking that both return the same result.
- Follow-ups
-
- The two versions differ by 3 %. Is that a real difference?
- The parser is slow only on the first file of a run. What do you report?
- How would you write the result up for the team?
From the lesson Measuring speed correctly with timeit
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Your CI fails a build when a benchmark of a core function is more than 10 % slower than last week's number. The check fails at random about once a week, and people now ignore it. How would you redesign it?
- What it tests
- Understanding noise on shared machines, measuring a baseline in the same run, choosing a statistic and a threshold from the measured spread, and making a performance check people trust.
- First step
- Stop comparing with a number stored on another day and another machine. Time the base commit and the change in the same job, on the same machine, in turns, and compare the two results with each other.
- Follow-ups
-
- How many repeats are enough, and which statistic do you compare?
- The CI machines are shared virtual machines. What changes?
- How do you catch a slowdown of 2 %?
From the lesson Measuring speed correctly with timeit
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A profile of a slow API endpoint shows 70 % of the cumulative time in json.dumps(), called 120,000 times for one request. What do you conclude, and what do you check before you try to make json.dumps() faster?
- What it tests
- Reading call counts as well as times, finding the caller that repeats work, allowing for the profiler's cost per call, and choosing the fix from the cause.
- First step
- Look at the number of calls before the time: 120,000 calls for one request means that something serialises in a loop. Find the callers with print_callers() and ask why each item is serialised separately.
- Follow-ups
-
- How much of the 70 % is the profiler's own overhead?
- The caller serialises each row to build a cache key. What do you change?
- When would a faster JSON library be the right answer?
From the lesson Profiling CPU time: cProfile and pstats
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Under cProfile a batch job runs three times slower than normal, and the profile blames a tiny helper that is called 5 million times. Is the helper the real problem, and how do you find out?
- What it tests
- Knowing that a deterministic profiler adds a cost to every call, confirming a hotspot without the profiler, and choosing between fewer calls, built-ins and inlining.
- First step
- Remember that cProfile records every call and return, so 5 million calls inflate the helper's share and slow the job down. Measure the job without the profiler, with and without the helper's work, to see what it really costs.
- Follow-ups
-
- How would a sampling profiler change the picture?
- The helper really is expensive. What are your options?
- How do you keep the fix from making the code unreadable?
From the lesson Profiling CPU time: cProfile and pstats
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
After a release, the 99th-percentile latency of a Python API doubled, but only in production, and you cannot restart the service under a profiler. How do you find out where the time goes?
- What it tests
- Choosing a sampling profiler for a live process, telling waiting from computing, comparing with the previous release, and working safely in production.
- First step
- Attach a sampling profiler to one running instance during a slow period instead of restarting it: on Python 3.14 a third-party tool such as py-spy, from 3.15 the standard library's profiling.sampling. Look first at whether the time is spent computing or waiting.
- Follow-ups
-
- The samples show most threads waiting on a lock. What next?
- How do you compare with the previous release?
- What do you check before attaching a profiler to production?
From the lesson Profiling CPU time: cProfile and pstats
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A Python web service's memory grows by about 200 MB a day until it is restarted. How do you find the cause, and how do you prove the fix?
- What it tests
- Measuring growth over time, comparing tracemalloc snapshots, finding what keeps objects alive, telling caches from leaks, and verifying a fix with the same measurement.
- First step
- Establish the shape first: memory every hour or every thousand requests, to tell steady growth from a cache that is filling up. Then compare two tracemalloc snapshots taken some hours apart on one instance, line by line.
- Follow-ups
-
- The top line is inside a library you use. What next?
- tracemalloc shows no growth, but the process keeps growing. Why could that be?
- How do you prove the fix before the next restart?
From the lesson Finding memory problems with tracemalloc
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A function decorated with functools.lru_cache(maxsize=None) caches product prices by product id and date, and memory grows every day. How do you choose a bound, and what do you measure to show that the bound is right?
- What it tests
- Seeing why an unbounded cache grows with distinct keys, measuring memory per entry, sizing from a budget, and checking the hit ratio with cache_info().
- First step
- Notice that the key contains the date, so every day adds a new entry per product and none is ever removed. Measure the memory of one entry with tracemalloc, then work out how many entries the memory budget allows.
- Follow-ups
-
- How do you measure the hit ratio before and after?
- Would a time-based expiry be better than a size bound here?
- What happens to memory when the bound is reached?
From the lesson Finding memory problems with tracemalloc
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A nightly job that processes a 4 GB CSV file is killed for running out of memory on an 8 GB machine. How do you find what takes the memory, and how do you make the job's memory independent of the file size?
- What it tests
- Measuring peak memory per stage, recognising whole-file loads and costly per-row objects, streaming with generators, compact representations, and testing the memory bound.
- First step
- Measure the peak, not the end state: run the job on a slice of the file with tracemalloc, call reset_peak() before each stage and read the peak after it. A peak that grows in proportion to the slice means a stage holds every row at once.
- Follow-ups
-
- The rows must be sorted. How do you avoid holding them all?
- Each row becomes a dict. How much does that cost?
- How do you test that the memory stays bounded?
From the lesson Finding memory problems with tracemalloc
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A daily report took 10 minutes last year and takes 90 minutes now, with three times as much data, and nothing in its code changed. What does that tell you, and what do you look for?
- What it tests
- Reading growth rates from measurements, recognising quadratic work, finding the scan inside a loop with a profile, fixing it with a data structure, and predicting future run times.
- First step
- Compare the growth with the data: three times the data took nine times as long, which is what work proportional to the square of the input does. Profile the job on a sample and look for a scan inside a loop.
- Follow-ups
-
- How long will it take next year with twice as much data again?
- The profile shows the time in list.index(). What do you change?
- How do you make sure it does not happen again?
From the lesson Making Python code faster: proven techniques
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A teammate added functools.lru_cache to a function that formats a price with tax, and the service became slower and used more memory. Explain how that can happen, and how you decide whether to keep the cache.
- What it tests
- Understanding what a cache costs on every call, hit ratios, keys that rarely repeat, the memory of entries, and measuring with cache_info() and fair timings.
- First step
- Check the hit ratio with cache_info(). If most calls are misses, every call now builds and hashes a key and stores a result on top of the work it did before, and a cheap function gains nothing in exchange.
- Follow-ups
-
- The hit ratio is 30 %. Is the cache worth keeping?
- How do you measure the cache's memory?
- When would you keep a cache even though it makes calls slower?
From the lesson Making Python code faster: proven techniques
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A function takes 15 % of a service's CPU time. A teammate proposes a rewrite that makes it four times as fast but much harder to read. How much does the service gain, and how do you decide?
- What it tests
- Working out a whole-program gain from a profile share, weighing it against readability and risk, looking for larger levers, and measuring the result.
- First step
- Compute the whole gain: the function's 15 % becomes 3.75 %, so the service saves about 11 % of its CPU time, and even an infinitely fast version would save only 15 %. Then compare that with the cost of the harder code and with other levers.
- Follow-ups
-
- The service runs on 40 servers. Does that change your answer?
- What would you measure before and after?
- How would you keep the readable version available?
From the lesson Making Python code faster: proven techniques
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes