Skip to content

Cython: C Performance with Python Syntax

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: python setup.py build_ext --inplace

# 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: ~1 second for n=1M
# 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: ~0.01 seconds (100x faster!)

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)