7 routes42 sections324 units

Unit 7.8 · about 5 h

49 / 324

Python and native code: dtype, stride, ownership and GIL contracts

An array's Python type does not specify its memory contract. The extension must agree with Python about representation, lifetime and errors before it can safely compute on raw storage.

  • explain

You can now

  • Explain a native function's dtype, byte order, rank, shape, stride and alignment requirements.
  • Distinguish copying input or output from borrowing storage, and identify which object keeps borrowed storage alive.
  • Explain exception translation and which parts of a call may run without the GIL on default CPython.

Covers

  • Python/native dtype, shape, stride, ownership, GIL and error contracts

Study, in this order

5 steps

  1. First steps
    pybind11 documentation

    Header and namespace conventions; Creating bindings for a simple function; Compiling the test cases; Keyword arguments

    Depth · Use pybind11 3.1 and your Python 3.14 development headers; skip exporting variables and unrelated C++11 syntax repair.

  2. NumPy and buffer contracts
    pybind11 documentation

    Buffer protocol; Arrays; Direct access; Memory view

    Depth · Skip Eigen and vectorizing functions. Do not copy the Matrix ownership sketch or the add_arrays raw-pointer loop unchanged: Matrix omits cleanup, and add_arrays accepts strided input but indexes it as contiguous. Use RAII storage and either validated contiguous input or stride-aware access.

  3. Conversion and return policies
    pybind11 documentation

    Return value policies; Additional call policies: Keep alive; Non-converting arguments

    Depth · Read ownership policies, not the crash wording as a guaranteed outcome of undefined behavior.

  4. GIL and free-threading contracts
    pybind11 documentation

    Global Interpreter Lock (GIL); Common Sources Of Global Interpreter Lock Errors; Free-threading support

    Depth · Apply GIL-held wording to the default build. Free-threaded Python was introduced in 3.13 and became supported in 3.14; the page's experimental wording is not the current CPython 3.14 status. Stop before sub-interpreter implementation.

  5. Exception translation
    pybind11 documentation

    Built-in C++ to Python exception translation table; Handling exceptions from Python in C++

    Depth · Skip custom translators and unraisable exceptions for this first binding.

Focus on

  • py::array_t<double> permits conversion by default; it does not mean no-copy or contiguous. For a strict 1-D native-endian float64 contract, use py::array_t<double, py::array::c_style> with py::arg(...).noconvert(), check rank and alignment, and reject unsupported input explicitly.
  • Buffer strides are signed byte distances. A reversed view can have a negative stride; pointer indexing is valid only when the accepted layout supports it. unchecked<N> handles strides but trusts your index bounds and the storage's continued validity.
  • Returning newly allocated NumPy-owned storage makes ownership explicit. A borrowed pointer instead needs an owner that stays alive and a no-reallocation contract; a reference return policy alone does not make dangling memory safe.
  • On default CPython 3.14, pybind11 does not implicitly release the GIL. Validate and acquire views while holding it, release only around native work, and reacquire before Python operations or reference-count changes. mod_gil_not_used promises thread safety; it does not provide it.

Reproduce closed-book

Against default CPython 3.14.4, NumPy 2.5.4 and pybind11 3.1, with g++ 15.2 -std=c++20 -O0 -g, bind a 1-D float64 sum using py::array_t<double, py::array::c_style> and .noconvert(). Reject ndim != 1 with py::value_error and a data pointer not aligned to alignof(double) with py::value_error; obtain unchecked<1>() before a scoped GIL release around the sum loop. Keep the input object alive and require no concurrent mutation. np.arange(6, dtype=np.float64) returns 15.0 and empty float64 input returns 0.0. float32 input, a list, a[::2] and a[::-1] raise TypeError; a.reshape(2,3) raises ValueError. Return a separately allocated py::array_t<double> copy in a second function, and test that it does not share memory with the input and survives deletion of the input. Also reject an unaligned contiguous float64 array constructed over a byte buffer with offset 1. Explain the owner and GIL state at each point rather than claiming a speedup.

Traps

  • Releasing the GIL does not prevent another caller from mutating the same NumPy storage. Require exclusive access or copy before release.
  • A C-contiguous array is not necessarily aligned. Check alignment before dereferencing it as double storage.
  • C++ exceptions can translate to Python exceptions; memory corruption and undefined behavior cannot reliably be contained that way.

Practice

The section mini-project covers this: expose the native function with dtype, shape, stride, alignment, ownership, GIL and exception contracts, and test accepted and rejected arrays plus output lifetime.

Self-test

  1. Does py::array_t<double> by itself guarantee a zero-copy contiguous float64 argument?

  2. Why is a[::2] unsafe for an unchecked raw double* loop using ptr[i]?

  3. What keeps a returned borrowed view valid?

  4. May a gil_scoped_release region create or destroy Python wrapper objects on the default build?

  5. What does declaring mod_gil_not_used mean for a free-threaded build?

Completion criterion

Stop when

Your strict-binding and independent-output tests pass, and you can explain each accepted or rejected input, the live storage owner and every GIL transition.

Mark passed, then open 7.9 Only mark this when you can do it without notes.

Leads to

Comes after

Resources for this unit

  • pybind11 documentation
    pybind11 projectrequiredimplementation
    Follow First steps and NumPy support to expose one small native function with explicit shape, dtype, stride, ownership, GIL and exception behavior.

Also in this section

  • AlgoMaster C++
    AlgoMasterprimarycourse
    Use the sections on compilation, pointers and references, memory, RAII and smart pointers, copy and move, STL containers, templates, errors, concurrency and paired exercises. Skip competitive-programming breadth already covered in Algorithms.
  • C++ Core Guidelines
    Standard C++ Foundation communityrequiredreference
    Use the Philosophy, Interfaces, Resource management, Expressions, Containers and concurrency rule groups to review ownership and safety decisions in the native component.
  • cppreference
    cppreference communitysupportreference
    Consult language lifetime, object model, value categories, memory model, atomics, containers and iterator invalidation while reading or modifying code.
  • Clang AddressSanitizer and UndefinedBehaviorSanitizer
    LLVMrequiredimplementation
    Compile seeded memory defects with AddressSanitizer and UndefinedBehaviorSanitizer, preserve the reports, repair the root cause and keep regression tests.
  • CMake tutorial
    CMakesupportimplementation
    Use the introductory steps for targets, libraries, testing and install rules. Produce one clean debug build and one optimized build without copied one-off commands.
  • Algorithmica - AoS and SoA
    Sergey Slotinsupportapplication
    Read the AoS and SoA experiment for a measured case where keeping each record together wins, the counterpart to a single-field scan.
  • `ld.so`: dynamic linker/loader
    Linux man-pages projectsupportreference
    Read DESCRIPTION, Dynamic string tokens and LD_DEBUG to distinguish link-time symbols from runtime shared-library discovery on Linux.
  • Memory Layout Transformations
    Intelsupportreference
    Read Overview and Topics through AOS Form and SOA Form for field-wise access and locality trade-offs. Skip Intel array-notation syntax and the LIBOR example.
  • GDB manual: Backtraces
    GNU Projectsupportreference
    Use section 8.2 for frame numbers, bt, bt -full and optimized-out arguments when inspecting a native failure.

Quick find

Search Michi

Type to search every route, section, unit, topic and resource.

Enter opens the first result Esc closes

Reference

Keyboard shortcuts

n
Continue to the first unit not passed
j / k
Next / previous unit
1 2 3
Toggle learned, practised, passed
/
Open search
?
Show this help
Esc
Close open panels