Cython¶
Overview¶
Cython compiles Python to C:
- Mixed Python/C: Write Python that compiles to C
- Type declarations: Hint types for optimization
- Performance: 10-100x speedup compared to pure Python
- Ease of use: Easier than manual C extensions
Basic Cython¶
Simple Cython Function¶
# fib.pyx
def fibonacci(int n):
"""Compute Fibonacci - typed for speed."""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
Compile and use:
# setup.py
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("fib.pyx")
)
# Build
# Use:
from fib import fibonacci
print(fibonacci(35)) # Instant!
Type Declarations for Speed¶
# fast_math.pyx
cdef double square(double x):
"""C function - very fast."""
return x * x
cdef double sum_squares(double[:] arr):
"""Use typed memoryview for array speed."""
cdef double total = 0.0
cdef int i
for i in range(arr.shape[0]):
total += square(arr[i])
return total
def sum_squares_py(arr):
"""Python wrapper."""
cdef double[:] view = arr
return sum_squares(view)
Typed Memory Views¶
Array Optimization¶
# array_ops.pyx
cdef double[:,::1] matrix_multiply(double[:,::1] A, double[:,::1] B):
"""Typed matrix multiply."""
cdef int m = A.shape[0]
cdef int n = B.shape[1]
cdef int k = A.shape[1]
cdef double[:,::1] C = zeros((m, n))
cdef int i, j, l
for i in range(m):
for j in range(n):
for l in range(k):
C[i, j] += A[i, l] * B[l, j]
return C
Interfacing with C¶
Calling C Functions¶
# math_interface.pyx
cdef extern from "math.h":
double sin(double x)
double cos(double x)
def py_sin(double x):
return sin(x)
def py_cos(double x):
return cos(x)
Performance Example¶
Pure Python vs Cython¶
# Pure Python
def compute_python(n):
total = 0
for i in range(n):
total += i ** 0.5
return total
# Time
# Cython
cdef compute_cython(long n):
cdef double total = 0.0
cdef long i
for i in range(n):
total += i ** 0.5
return total
# Time
-
Real-World ML Example¶
Custom Loss in Cython¶
# custom_loss.pyx
import numpy as np
cimport numpy as np
cdef extern from "math.h":
double exp(double x)
def huber_loss(double[::1] predictions, double[::1] targets, double delta):
"""Huber loss in Cython."""
cdef int n = predictions.shape[0]
cdef double loss = 0.0
cdef double error
cdef int i
for i in range(n):
error = predictions[i] - targets[i]
if abs(error) <= delta:
loss += 0.5 * error * error
else:
loss += delta * (abs(error) - 0.5 * delta)
return loss / n
Summary: When to Use Cython¶
| Scenario | Use Cython |
|---|---|
| Tight loops with math | |
| Array operations | |
| I/O-bound code | (threading better) |
| Numerical compute | |
| Complex logic | ~ (maybe) |
-
Related Topics¶
- [03 Jit Compilation & Optimization](/05-py3/09-bytecode-and-execution/(03-jit-compilation-optimization/) - JIT as alternative
- 00 Readme - Performance patterns
- [01 Ctypes & Cffi](/05-py3/04-c-extensions-and-ffi/(01-ctypes-cffi/) - Other FFI approaches