4 min read
Aggregate sequences with functools.reduce vs a plain for loop, with sum / math.prod as reference arms. This lab reports element ops/s on Linux localhost.
Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. Data: ints 1..200, repeated 5000 times.
Verdict up front: builtin sum ~405562698 elem-ops/s; for-loop add ~65071639; reduce(operator.add) ~59179970; reduce(lambda) ~29566082. Product: math.prod ~29886074 vs reduce mul ~24946185.
| Arm | Pattern |
|---|---|
reduce(operator.add, …) | fold sum |
for + += | explicit sum |
sum(data) | C builtin |
reduce(operator.mul, …) | fold product |
for + *= | explicit product |
math.prod | stdlib product |
reduce(lambda a,b: a+b, …) | slow fold |
Seven rounds, p50. Metric: (n_outer × n_elems) / p50_s.
Script: lab-evidence/142-reduce-vs-loop/results/run_lab.py.
| Arm | elem-ops/s |
|---|---|
| builtin sum | 405562698 |
| for-loop add | 65071639 |
| reduce operator.add | 59179970 |
| math.prod | 29886074 |
| reduce operator.mul | 24946185 |
| for-loop mul | 24235359 |
| reduce lambda add | 29566082 |
Sums equal (True); prods equal (True).
sum() (order-of-magnitude win here).math.prod over hand-rolled reduce.reduce(operator.*) ≈ a tight loop; reduce(lambda…) paid extra call overhead (~29566082).+ on ints.reduce(lambda a, b: a + b, …) fell to ~29566082 elem-ops/s vs operator.add ~59179970. If you must fold, pass operator functions, not lambdas.
Custom reducers (merge dicts, combine metrics structs) are clearer as reduce(combine, items, init) than a 15-line loop — just do not use it to reinvent sum. On this box the loop edged reduce for add (~65071639 vs ~59179970); builtin sum still dominated both.
Document “use sum/prod unless combiner is custom” in style guides so reviews stop bikeshedding reduce micro-optimizations.
sum at ~405562698 elem-ops/s is not a fair peer to a Python-level fold — it is the reminder that CPython already optimized the common case. Reaching for reduce to “look functional” on integer sums costs readability and loses about an order of magnitude versus sum here. For products, math.prod ~29886074 still beat both reduce and the hand loop.
If a code search finds reduce(operator.add, nums, 0) in a hot metrics path, swap to sum(nums) and re-bench once — that single change usually ends the discussion.
math.prod exists (3.8+).Evidence: summary.json, summary.txt.
One Linux box, small int lists. Not numpy reductions, not parallel folds.
Prefer readable builtins first; reach for reduce only when the combiner is genuinely custom and tested.
Keep microbench scripts beside production PRs so claims stay reproducible on the same box class.
Use sum / math.prod for numeric aggregates (~405562698 / ~29886074 elem-ops/s). reduce(operator.add) ~59179970 tracks a for-loop; avoid lambda reduce in hot code.
Lab evidence
Ran the included reduce-versus-loop benchmark on Linux localhost with Python 3.13.5. Seven rounds used 5,000 outer repetitions over 200 integers, and I compared p50 element operations per second for sum and product. Builtin sum was fastest; the for-loop edged operator.add reduce, while lambda reduce was slower. Products showed math.prod ahead of reduce and the hand loop. Results were reproducible on this box.
Insertion order is a language guarantee, not an implementation accident.
Summary12 Oct 20261 min read
PEP 779 promoted the no-GIL build from experimental to officially supported in 3.14. It is still opt-in, and making it the default is a later phase.
Summary11 Oct 20263 min read
Five years after 3.10.0 shipped, CPython 3.10 stopped getting security fixes. If 3.10 is still running in production, here is what changes and where to move next.
Summary10 Oct 20263 min read