# Python performance: native NumPy loops, callbacks and conversion costs

Published: 2026-10-17

A controlled CPU-only numerical test follows identical values through CPython loops, NumPy arithmetic, a Python callback wrapper and a compiled Numba loop. Ready-input NumPy had a large lead, while conversion from and back to Python lists reduced that advantage to a near tie. The episode explains the execution boundary and how to measure the result an application needs.

Canonical: https://thedailydiff.dev/video/2026-10-17-python-native-loops/

## What this video covers

- CPython compiles source to bytecode. Repeated interpreter/object work and a typed native numeric loop are different execution arrangements.
- Numeric NumPy arithmetic can hand typed batches to compiled operations; the arithmetic loop still exists, and views can have noncontiguous layouts.
- Measure conversion and required output representation. In this run, converting Python lists into arrays and back erased the large ready-array lead.
- Small workloads can favor ordinary Python because fixed array-call costs matter. The eight-value comprehension won this measured comparison.
- np.vectorize is a convenience wrapper that calls Python for successive elements; a batch interface alone does not establish native numerical execution.
- Compiled Python-syntax loops can also be fast. Include cold compilation/setup costs when they matter to the caller.
- Native execution, CPU SIMD, multithreading and GPU backends are separate ideas. Available CPU features do not trace the instructions used in a particular run.

## Chapters

- 0:00 Why can the same CPU finish sooner?
- 0:52 What work does a Python loop repeat?
- 1:38 Where does NumPy move the loop?
- 2:13 Does fast mean GPU or many CPU cores?
- 2:49 When does the impressive speedup disappear?
- 3:28 Which boundaries belong in the benchmark?
- 4:00 Can a compiled Python loop be fast too?
- 4:56 What does np.vectorize actually execute?
- 5:41 What should you change in your code?

## Transcript

### Why can the same CPU finish sooner?

0:00 You think Python needs a new language to get fast. The same CPU ran our batch faster after the loop moved. Where did the work go? When does the speedup vanish? Which shortcut brings Python back? This is The Daily Diff, under the hood. Keep an eye on that shortcut. CPython is the standard Python implementation.

0:17 It compiles your source into bytecode, then runs those instructions in an interpreter. NumPy is a numerical library whose arrays hold values with a declared data type. Its built in arithmetic can hand a whole batch to compiled operations. Take a million floating point values, square each one, then add the original value. Every version gets identical values and produces matching answers. The values are small enough to keep rounding differences out of the race.

0:40 On this machine, the comprehension took about thirty three milliseconds. NumPy took less than one. Both ran on the CPU. That's useful evidence for this workload, and a terrible obituary for a programming language. Start with the ordinary loop.

### What work does a Python loop repeat?

0:53 Python gets the next value, performs the arithmetic, and appends the answer. Then it does that again. The multiplication is cheap. Arranging the next multiplication, and handling the Python objects around it, adds work every time. This is the tested function's actual disassembly. Iteration, multiplication and addition appear as bytecode instructions. Python versions and interpreters make different choices.

1:14 These timings belong to the version on screen, which is Python three point ten. A Python list stores references to objects, so different kinds of things can share a list. Our arithmetic job uses numbers throughout. It pays for a general arrangement while requesting a repetitive service. Imagine ordering the same coffee a million times and filling out a fresh form for every cup. The coffee machine is fine.

1:36 The paperwork has developed its own career.

### Where does NumPy move the loop?

1:38 NumPy changes the contract. This array holds fixed size floating point values with one declared type. Our array is contiguous, so its values sit together in memory. Views can have other layouts. The arithmetic operation knows what kind of data it is receiving. The call hands the batch to a native operation. A loop still visits the values inside compiled code. It can reuse knowledge of the type across that work.

2:00 Python remains the person handing over the job. Itamar Turner Trauring explains the distinction in Python Speed. Batch interfaces, native implementations and processor vector instructions are separate things. The function name supplies very little evidence.

### Does fast mean GPU or many CPU cores?

2:13 This result used NumPy on the CPU. A GPU backend such as CuPy chooses a different home for arrays and operations. Buying a GPU leaves this ordinary Python loop running where it already ran. CPU vector instructions can process several values together. NumPy has machinery for choosing supported kernels. Our runtime reports available features, but doesn't trace this expression's instructions. Giving the entire measured gain to vector instructions would oversell the evidence.

2:40 The GPU shopping cart can stay closed. There is something deeply satisfying about a performance fix that also cancels a purchase. Unfortunately, the benchmark continues.

### When does the impressive speedup disappear?

2:49 With eight values, the comprehension won. Calling the array machinery cost more than the tiny job. Larger ready arrays pulled ahead. We used repeated trials and a warmed process. Newer Python builds can change the crossover. Then change what the application needs. Begin with a Python list and finish with a fresh Python list. NumPy must convert the input into an array and turn the answer back into Python

3:11 objects. Those steps belong on the bill if your application needs them. At a million values, that complete path took about twenty eight milliseconds. The comprehension took about twenty eight too. Our dramatic lead became a near tie. The fast part was real. The advantage depended on letting the data stay in its numerical form.

### Which boundaries belong in the benchmark?

3:28 Follow the data. If the next operation accepts an array, keep the array. If you repeatedly convert between lists and arrays, measure those crossings. Stopping before an expensive handoff gives accurate numbers for the wrong question. Multiplying this array creates a temporary result. Adding the original values creates the output. Native execution still needs memory and moves data.

3:47 Some operations accept an output buffer, reducing allocations when your code can safely reuse it. One pretty line can order several large buffers. Python Speed calls out this problem too. The allocator is remarkably indifferent to your aesthetic.

### Can a compiled Python loop be fast too?

4:00 You can also keep loop syntax and change its execution. Numba can compile supported numerical Python functions into native code. Here is the loop from our test with its compiler decorator. It still says for. The expensive interpreter path has changed. After compilation, this loop took less than half a millisecond with ready arrays. Compilation itself took about a second in this run. That setup cost was excluded from the repeated timings,

4:22 so a tiny one off job deserves a different comparison. Numba's own guide is happy to show explicit loops. Starting a language migration before finding the hot path is an elaborate way to avoid opening a profiler. First find what consumes the time. Network waits, object parsing and bulk arithmetic call for different fixes. Native array operations help when the calculation and data fit.

4:43 Measure the result your caller actually needs. Now for that shortcut. You have a Python function and want it to accept an array. A wrapper named vectorize sounds like the missing performance button. Its documentation has an important sentence waiting.

### What does np.vectorize actually execute?

4:56 NumPy says its vectorize wrapper is provided primarily for convenience. Its implementation is essentially a for loop. The wrapper accepts batches, but keeps calling your Python function for successive elements. The interface got shorter. The execution boundary stayed busy. Watch the same values come back through the Python callback. With ready arrays, this version took about eighty seven milliseconds. It was slower than our comprehension.

5:18 A useful broadcasting wrapper can preserve the exact per element work you were trying to remove. Check the implementation behind a batch interface, keep compatible data in its useful form, and include conversion in the comparison. Native, callback and compiled loops can look similar from the caller's chair. The name vectorize made a promise your stopwatch never signed. Keep it when convenience is what you need. For performance, check the loop that runs.

### What should you change in your code?

5:41 My verdict is ship it for batching a measured numerical hot path. Keep ordinary loops when they already work well. Check where the work happens and what the next caller needs. Subscribe, hit the bell, and tell me in the comments if you'd have stamped it differently. And that's the diff for today. I'm Niko from Axrisi. Merge responsibly.

## Sources

- [Universal functions (ufunc) basics — NumPy 2.2](https://numpy.org/doc/2.2/user/basics.ufuncs.html) — NumPy
- [The N-dimensional array — NumPy 2.2](https://numpy.org/doc/2.2/reference/arrays.ndarray.html) — NumPy
- [numpy.vectorize — convenience and performance notes](https://numpy.org/doc/2.2/reference/generated/numpy.vectorize.html) — NumPy
- [CPU/SIMD optimizations — NumPy 2.2](https://numpy.org/doc/2.2/reference/simd/index.html) — NumPy
- [dis — Disassembler for Python bytecode](https://docs.python.org/3.10/library/dis.html) — Python Software Foundation
- [CPython 3.10.12 list representation](https://github.com/python/cpython/blob/v3.10.12/Include/cpython/listobject.h) — CPython contributors
- [The limits of Python vectorization as a performance technique](https://pythonspeed.com/articles/vectorization-python-alternatives/) — Itamar Turner-Trauring / Python⇒Speed
- [Performance Tips](https://numba.readthedocs.io/en/stable/user/performance-tips.html) — Numba
- [Basics of CuPy](https://docs.cupy.dev/en/stable/user_guide/basic.html) — CuPy
