Tüm eğitimler
TEKNİK REHBER CYTHON Python → C 2026

Cython
Python'u C hızında çalıştırmak.

Python'un üst-kümesi bir dilde yaz, C'ye derlet, uzantı modülü olarak import et. Statik tiplemeden typed memoryview'a, nogil paralellikten cdef extern ile C kütüphane sarmaya — 12 bölümde baştan sona.

00 Cython nedir, neden

Python yavaş — ama tam olarak nerede ve ne kadar? Cython bu sorunun cerrahi cevabı.

CPython, her Python satırını bytecode'a derler ve bu bytecode'u yorumlayan bir C döngüsünde çalıştırır. Her nesne bir PyObject* işaretçisidir; her değişken erişimi referans sayacı günceller; her aritmetik işlem tip denetimi yapan bir C fonksiyonu çağırır. Bu esnekliğin bedeli hızdır.

Cython bu denklemi tersine çevirir: .pyx uzantılı dosyalarda Python sözdiziminin üstüne C tip bildirimleri eklenir. Cython derleyicisi bu dosyayı C koduna çevirir; C kodu daha sonra normal bir C derleyicisi ile derlenerek bir CPython uzantı modülü (.so / .pyd) üretilir. Sonuç Python'dan import edilir, ama içeride saf C çalışır.

  1  hello.pyx          ← Python + C tip bildirimleri
        │
        ▼  cython
  2  hello.c            ← ~2000 satır üretilmiş C kodu
        │
        ▼  gcc / clang
  3  hello.cpython-311-x86_64-linux-gnu.so
        │
        ▼  import hello
  4  Python içinde C hızında çalışan modül

Kademeli dönüştürme

Cython'un en güçlü özelliği: bir .py dosyasını doğrudan .pyx olarak derleyebilirsiniz. Hiçbir tip eklemeden bile küçük bir hız kazancı gelir. Sonra sadece dar boğaz olan fonksiyonlara tip bildirimi eklenirse kazanç dramatikleşir. Tüm projeyi yeniden yazmak gerekmez.

Alternatifler tablosu

Cython .pyx → C → .so, AOT derleme. Kademeli dönüştürme, C API'ye tam erişim. Build pipeline karmaşık.
numba @jit ile runtime LLVM derlemesi. Kurulum gerekmez, sadece dekoratör. NumPy odaklı, genel Python nesnelerine zayıf.
ctypes Stdlib FFI; mevcut .so/.dll'i runtime'da çağırır. Derleme gerekmez, ama yavaş ve manuel tip yönetimi yorucu.
cffi ctypes'tan temiz API. PyPy dostu. AOT veya ABI modu. Cython kadar performans kazanmaz.
pybind11 C++ kütüphanelerini Python'a bağlamak için. Header-only, modern C++11. C++ yazmak zorundasın.
PyO3 Rust → Python bağlaması. Cargo ile derle, Rust güvenliği elde et. Rust öğrenmek gerekiyor.
nanobind pybind11'in halefi; daha hızlı derleme, daha küçük binary. Henüz genç, API değişiyor.

Ne zaman Cython, ne zaman değil

CYTHON DOĞRU SEÇİM
  • Saf Python döngüsü dar boğaz — NumPy vektorizasyonu uygulanamıyor
  • Mevcut bir C kütüphanesini Python'a sarmak istiyorsun
  • NumPy işlem sonrası element-wise hesaplama gerekliyse
  • GIL release ederek gerçek paralel CPU kullanmak istiyorsan
  • Mevcut Python kodunu adım adım, modül modül hızlandırmak istiyorsun
CYTHON GEREKMİYOR
  • Zaten NumPy vektorizasyonu kullanıyorsun — ekstra kazanç marjinal
  • I/O darboğazı var — disk veya ağ bekliyorsa CPU optimizasyonu fark etmez
  • Küçük script, kısa ömürlü — build pipeline getirisi negatif
  • Cross-compile derdi yok — wheel oluşturmak her platform için ayrı CI gerektirir

01 Kurulum ve ilk derleme ortamı

Cython kendi başına derleme yapmaz — bir C derleyicisine ve Python geliştirici başlıklarına ihtiyaç duyar.

Sistem gereksinimleri

Linux sudo apt install gcc python3-dev python3-venv (Debian/Ubuntu)
macOS xcode-select --install — Clang + Python başlıkları birlikte gelir
Windows Visual Studio Build Tools gerekli: C++ build tools workload seçili olmalı. Python sürümüyle eşleşen MSVC versiyonu.

Sanal ortam oluştur ve aktif et

bash
mkdir cython-demo && cd cython-demo
python3 -m venv .venv
source .venv/bin/activate

Windows: .venv\Scripts\activate

Cython'u kur

bash
pip install cython setuptools
cython --version   # Cython version 3.x.x
cython
Derleyicinin kendisi: .pyx dosyasını C koduna çevirir
setuptools
Python extension modüllerini build etmek için standart araç; setup.py ve pyproject.toml desteği

Hızlı doğrulama

bash
python -c "import Cython; print(Cython.__version__)"
# 3.0.11  (ya da benzer)
python -c "import sysconfig; print(sysconfig.get_config_var('CC'))"
# gcc  — derleyici bulundu
NEDEN python3-dev

Cython'un ürettiği C kodu Python.h başlık dosyasını include eder. Bu dosya Python yorumlayıcısının C API'sidir; runtime paketinde değil, geliştirici paketinde (python3-dev / python3-devel) bulunur.

02 İlk .pyx dosyası ve setup.py derlemesi

Sıfırdan çalışan bir uzantı modülü — üç dosya, tek komut.

hello.pyx

hello.pyx
def greet(name):
    return f"Merhaba, {name}!"

def square(x):
    return x * x

Şu an bu dosya tamamen geçerli Python — Cython'a özgü hiçbir şey yok. Bunu bile derleyebiliriz.

setup.py

setup.py
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("hello.pyx", language_level=3)
)
cythonize()
.pyx dosyasından Extension nesnesi üretir; Cython derleyicisini çağırır
language_level=3
Python 3 semantics kullan (her zaman açık olsun — varsayılan hâlâ 2'dir)

Derleme

bash
python setup.py build_ext --inplace
# Running build_ext
# building 'hello' extension
# gcc -shared -fPIC ... -o hello.cpython-311-x86_64-linux-gnu.so
build_ext
Extension modülü derle
--inplace
Derleme çıktısını build/ altına değil, kaynak dosyanın yanına yaz

Bu komut iki şey üretir:

hello.c Cython'un ürettiği C kodu (~1800–2500 satır). Silme — yeniden build gerekirse yeniden üretilir
hello.cpython-311-x86_64-linux-gnu.so Derlenmiş uzantı modülü. Platform ve Python sürümüne özgü

Import et ve çalıştır

bash
python -c "import hello; print(hello.greet('Ali')); print(hello.square(7))"
Merhaba, Ali!
49

Geliştirme modu: pyximport

Her değişiklikte setup.py çalıştırmak yerine pyximport kullanılabilir — import anında otomatik derler:

python
import pyximport
pyximport.install(language_level=3)
import hello   # ilk kullanımda derlenir
print(hello.greet('Ali'))
DİKKAT

pyximport sadece geliştirme için — C başlıkları, özel flag'ler veya ek C kaynakları gerektiren modüller için çalışmaz. Production build'lerde her zaman setup.py veya pyproject.toml kullanın.

Üretilen C koduna bakmak

bash
wc -l hello.c
2156 hello.c
head -30 hello.c
# /* Generated by Cython 3.x.x */
# #define PY_SSIZE_T_CLEAN
# #include "Python.h"
# ...

Bu kodu okumak zorunda değilsiniz — ama "ne üretildi" merakı için açmak normaldir. Bölüm 09'da annotation HTML daha insancıl bir alternatif sunacak.

03 Statik tipler: def / cdef / cpdef

Cython'un performans kazancının büyük kısmı tip bildirimlerinden gelir — Python'un runtime tip sorgusu ortadan kalkar.

Üç fonksiyon çeşidi

def Normal Python fonksiyonu. Python tarafından çağrılabilir. Her argüman PyObject* geçişi yapar. En yavaş.
cdef Saf C fonksiyonu. Sadece Cython veya C kodundan çağrılabilir — Python'dan erişilemez. En hızlı.
cpdef İkili sürüm. Python'dan çağrıldığında def gibi, Cython içinden çağrıldığında cdef gibi davranır.
fib.pyx
# Versiyon A — saf Python (tip yok)
def fib_py(n):
    a, b = 0, 1
    while b < n:
        a, b = b, a + b
    return b

# Versiyon B — def + cdef lokal değişkenler
def fib_typed(long n):
    cdef long a = 0, b = 1
    while b < n:
        a, b = b, a + b
    return b

# Versiyon C — saf C fonksiyonu (Python'dan çağrılamaz)
cdef long _fib_c(long n):
    cdef long a = 0, b = 1
    while b < n:
        a, b = b, a + b
    return b

# Python wrapper — cdef fonksiyonunu dışarıya açar
cpdef long fib(long n):
    return _fib_c(n)

Yaygın C tipleri

int
32-bit işaretli tamsayı (platform bağımlı olabilir; kesin boyut için int32_t kullanın)
long
64-bit tamsayı (Linux/macOS 64-bit). Fibonacci için doğal seçim
double
64-bit float. Python'un varsayılan float tipiyle birebir eşleşir
float
32-bit float. NumPy float32 dizileriyle uyumlu
bint
"Boolean int" — C int olarak saklanır, Python bool olarak döner
char
Tek byte karakter; string C API'siyle çalışırken sık kullanılır
unsigned int
İşaretsiz 32-bit; negatif sayı imkânsızsa bellek ve performans avantajı
size_t
Bellek boyutu tipi; malloc/memcpy argümanları için standart

Fibonacci benchmark

bash
python -m timeit -s "import fib" "fib.fib_py(1_000_000)"
1000000 loops, best of 5: 4.12 µs per loop

python -m timeit -s "import fib" "fib.fib_typed(1_000_000)"
1000000 loops, best of 5: 0.31 µs per loop

python -m timeit -s "import fib" "fib.fib(1_000_000)"
1000000 loops, best of 5: 0.09 µs per loop

Saf Python → tipli def → saf cdef: yaklaşık 13×, ardından ek daha. Fibonacci küçük bir örnektir; döngü sayısı arttıkça fark büyür.

KURAL

Public API için cpdef; sadece iç hesaplama için cdef; Python nesneleri alan/döndüren arayüz için def. İç döngü ne kadar saf C olursa performans o kadar artar.

04 C tipleri, struct, ctypedef, pointer

Cython, C'nin tip sistemine doğrudan erişim verir — struct, union, pointer ve bellek yönetimi.

struct ve ctypedef

geometry.pyx
cdef struct Point:
    double x
    double y

ctypedef double real_t   # tip takma adı

cdef union IntOrFloat:
    int    i
    float  f

cpdef double distance(Point a, Point b):
    cdef double dx = a.x - b.x
    cdef double dy = a.y - b.y
    from libc.math cimport sqrt
    return sqrt(dx*dx + dy*dy)

def test_distance():
    cdef Point p1 = Point(x=0.0, y=0.0)
    cdef Point p2 = Point(x=3.0, y=4.0)
    return distance(p1, p2)   # → 5.0

Pointer ve manuel bellek yönetimi

memory.pyx
from libc.stdlib cimport malloc, free
from libc.string cimport memset

def make_array(int n):
    cdef int* arr
    cdef int  i

    arr = <int*>malloc(n * sizeof(int))
    if arr == NULL:
        raise MemoryError("malloc başarısız")

    for i in range(n):
        arr[i] = i * i        # pointer aritmetiği: arr[i] = *(arr + i)

    result = [arr[i] for i in range(n)]

    free(arr)                 # MUTLAKA serbest bırak
    return result
<int*>
Cython cast sözdizimi: C'nin (int*) ifadesine denk gelir
sizeof(T)
C'nin sizeof operatörü — tip veya değişken alır
NULL
Null pointer sabiti; malloc başarısız olursa döner
free(ptr)
Her malloc için bir free — yoksa bellek sızıntısı olur

Hazır libc cimport'ları

cython
from libc.stdlib cimport malloc, free, realloc, qsort
from libc.string cimport memcpy, memset, strlen, strcmp
from libc.math   cimport sqrt, sin, cos, log, exp, fabs, pow
from libc.stdio  cimport printf, fprintf, fopen, fclose
EXCEPTION SAFETY

malloc çağrısı yaparken exception fırlatılırsa free atlıyabilir. Güvenli pattern: değişkeni NULL ile başlat, sonunda if ptr != NULL: free(ptr). Büyük modüllerde cdef class ve __dealloc__ daha güvenli bir alternatif sunar (bkz. Bölüm 05).

05 Extension types: cdef class

Python class'ının C-backed versiyonu: attribute'lar Python dict'te değil C struct'ta saklanır.

vector.pyx
from libc.math cimport sqrt

cdef class Vector2D:
    cdef public double x    # Python'dan okuma ve yazma
    cdef public double y

    def __cinit__(self, double x, double y):
        # C-level init — her koşulda çalışır, override edilemez
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector2D({self.x}, {self.y})"

    cpdef double magnitude(self):
        return sqrt(self.x * self.x + self.y * self.y)

    cpdef double dot(self, Vector2D other):
        return self.x * other.x + self.y * other.y

    def normalize(self):
        cdef double m = self.magnitude()
        if m == 0.0:
            raise ValueError("Sıfır vektörü normalize edilemez")
        return Vector2D(self.x / m, self.y / m)

Attribute erişim belirleyicileri

cdef double x
Private — Python tarafından erişilemez (varsayılan)
cdef readonly double x
Python'dan okuma var, yazma yok
cdef public double x
Python'dan hem okuma hem yazma

Lifecycle metodları

__cinit__ C-level initialize. __init__'den önce çalışır. Alt sınıf tarafından override edilemez; her zaman çağrılır. malloc çağrıları buraya.
__init__ Normal Python init. Sonra çalışır, override edilebilir.
__dealloc__ C-level destruktör. Python GC devreye girmeden önce çalışır. free() çağrıları buraya — Python exception güvenli.

Manuel bellek yöneten cdef class örneği

buffer.pyx
from libc.stdlib cimport malloc, free

cdef class IntBuffer:
    cdef int* data
    cdef public int size

    def __cinit__(self, int size):
        self.size = size
        self.data = <int*>malloc(size * sizeof(int))
        if self.data == NULL:
            raise MemoryError()

    def __dealloc__(self):
        if self.data != NULL:
            free(self.data)   # nesne yok edildiğinde otomatik çalışır

    def __setitem__(self, int i, int val):
        if i < 0 or i >= self.size:
            raise IndexError(i)
        self.data[i] = val

    def __getitem__(self, int i):
        if i < 0 or i >= self.size:
            raise IndexError(i)
        return self.data[i]

cdef class kısıtlamaları

KISITLAMALAR
  • Monkey-patch yapılamaz — v.new_method = lambda: ... çalışmaz
  • Sadece cdef class'tan veya object'ten kalıtım alınabilir
  • C-tipli attribute'lar __dict__'te görünmez; public yapılmadan pickle edilemezler
  • Çoklu kalıtım kısıtlı: birden fazla cdef class'tan miras alınamaz

06 cdef extern: C kütüphanesi sarmak

Mevcut bir C kütüphanesini hiç Python yazmadan Python'a açmak — cdef extern from ile.

libc fonksiyonlarını sarmak

mathwrap.pyx
cdef extern from "math.h":
    double sin(double x) nogil
    double cos(double x) nogil
    double sqrt(double x) nogil

def py_sin(double x):
    return sin(x)   # doğrudan C sin() çağrısı

Kendi C kodunu sarmak

Önce C fonksiyonunu yaz:

stats.c
// stats.c
#include "stats.h"
#include <stdlib.h>

double mean(const double* data, int n) {
    double sum = 0.0;
    for (int i = 0; i < n; i++) sum += data[i];
    return sum / n;
}

double variance(const double* data, int n) {
    double m = mean(data, n), v = 0.0;
    for (int i = 0; i < n; i++) v += (data[i]-m)*(data[i]-m);
    return v / n;
}
stats.h
double mean(const double* data, int n);
double variance(const double* data, int n);
pystats.pyx
cdef extern from "stats.h":
    double mean(const double* data, int n) nogil
    double variance(const double* data, int n) nogil

def py_mean(list data):
    cdef int n = len(data)
    cdef double* arr = <double*>malloc(n * sizeof(double))
    from libc.stdlib cimport malloc, free
    if arr == NULL: raise MemoryError()
    try:
        for i in range(n): arr[i] = data[i]
        return mean(arr, n)
    finally:
        free(arr)

setup.py — C kaynağını derlemeye dahil et

setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize

ext = Extension(
    "pystats",
    sources=["pystats.pyx", "stats.c"],   # hem .pyx hem .c
    libraries=["m"],                        # -lm (libm)
)
setup(ext_modules=cythonize([ext], language_level=3))
sources
Derlemeye katılacak tüm kaynak dosyaları — .pyx ve .c birlikte
libraries
Link edilecek sistem kütüphaneleri ("m" = libm, "z" = libz, vb.)
include_dirs
Başlık dosyaları arama yolları (["./include"])
library_dirs
Kütüphane arama yolları (["/usr/local/lib"])
extra_compile_args
Ekstra derleyici flagleri (["-O3", "-march=native"])

Hata işleme: except bildirimleri

cython
# C fonksiyon -1 dönerse Python exception oluştur
cdef int might_fail(int x) except -1:
    ...

# Herhangi bir dönüş değerinde PyErr kontrolü yap (yavaş)
cdef int uncertain(int x) except? -1:
    ...

# void fonksiyon — her çağrıdan sonra kontrol et
cdef void also_might_fail(int x) except*:
    ...

Struct sarmak

cython
cdef extern from "mylib.h":
    ctypedef struct MyRecord:
        int    id
        double value
        char*  label

    MyRecord* record_new(int id, double val) except NULL
    void record_free(MyRecord* r)

07 NumPy interop ve typed memoryview

NumPy dizilerine sıfır kopyalı erişim — Cython'un gerçek kullanım anı.

Typed memoryview nedir?

Typed memoryview, Python'un buffer protokolünü uygulayan her nesneye (NumPy array, array.array, bytes, bytearray) C seviyesinde, kopyasız erişim sağlar. NumPy import etmek zorunda bile değilsiniz — sadece bir array gelirse çalışır.

matmul.pyx
def matmul(double[:, :] A, double[:, :] B, double[:, :] C):
    """C = A @ B (in-place, C önceden sıfırlanmış olmalı)"""
    cdef int i, j, k
    cdef int M = A.shape[0]
    cdef int K = A.shape[1]
    cdef int N = B.shape[1]
    for i in range(M):
        for j in range(N):
            for k in range(K):
                C[i, j] += A[i, k] * B[k, j]
bash — kullanım
import numpy as np
import matmul

A = np.random.rand(100, 100)
B = np.random.rand(100, 100)
C = np.zeros((100, 100))

matmul.matmul(A, B, C)   # sıfır kopya — A,B,C bellekte tutulur

Contiguity (bellek düzeni)

double[:, :]
Herhangi bir bellek düzeni (strided). Daha genel ama compiler daha az optimize edebilir
double[:, ::1]
C-contiguous (satır-başat). Son boyut stride=1. NumPy default. Compiler için en iyi
double[::1, :]
Fortran-contiguous (sütun-başat). BLAS/LAPACK kütüphaneleriyle uyumlu
double[::1]
1D contiguous memoryview
matmul_fast.pyx — contiguous versiyon
# cython: boundscheck=False, wraparound=False

def matmul_fast(double[:, ::1] A,
                double[:, ::1] B,
                double[:, ::1] C):
    cdef int i, j, k
    cdef int M = A.shape[0], K = A.shape[1], N = B.shape[1]
    for i in range(M):
        for j in range(N):
            for k in range(K):
                C[i, j] = C[i, j] + A[i, k] * B[k, j]

Benchmark (100×100, tek iş parçacığı)

bash
# Saf Python döngüsü:
~8500 µs  (85 ms)

# Typed memoryview, boundscheck açık:
~180 µs

# Typed memoryview [:, ::1], boundscheck=False:
~38 µs

# NumPy @ operatörü (BLAS):
~6 µs   ← BLAS paralel SIMD kullandığı için kazanır
KURAL

NumPy'nin kendi operatörleri (@, np.dot, np.sum) zaten BLAS/LAPACK üzerinde çalışır — paralel SIMD var. Cython memoryview, NumPy'nin ifade edemediği karmaşık element-wise logic için parlar: koşullular, birden fazla dizi üzerinde özel işlem, erken çıkış gerektiren döngüler.

Eski yöntem (legacy)

legacy.pyx
# Bu yöntem çalışır ama artık önerilmiyor
import numpy as np
cimport numpy as np

def old_style(np.ndarray[np.float64_t, ndim=2] A):
    cdef int i, j
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            A[i, j] *= 2.0

Bu sözdizimi hâlâ geçerli ama typed memoryview daha az import, daha geniş uyumluluk ve daha temiz hata mesajları verir. Yeni kod için memoryview kullanın.

08 GIL ve paralellik: nogil + prange

GIL'i bırak, birden fazla çekirdeği kullan — ama kurallara uy.

GIL hatırlatması

CPython'un Global Interpreter Lock'u tek seferde yalnız bir thread'in Python bytecode'u çalıştırmasına izin verir. Bu, saf Python ile gerçek paralel CPU işlemi imkânsız kılar. Cython bu kısıtlamayı kısmen aşar: Python nesnelerine dokunmayan fonksiyonlar GIL'siz çalışabilir.

nogil fonksiyon

parallel.pyx
from libc.math cimport sin, sqrt

# GIL gerektirmeyen saf hesaplama
cdef double heavy_compute(double x) nogil:
    return sin(x) * sqrt(x + 1.0)

# Python'dan çağrılabilir wrapper
def compute_single(double x):
    return heavy_compute(x)   # GIL otomatik tutulur

# Uzun hesaplama — GIL'i geçici bırak
def compute_many(double[:] data, double[:] out):
    cdef int i, n = data.shape[0]
    with nogil:                # bu blok içinde GIL yok
        for i in range(n):
            out[i] = heavy_compute(data[i])

nogil içinde yasak ve izinli

YASAK Python nesnesi oluşturmak (list(), dict(), str), Python attribute erişimi, raise, print, Python fonksiyonu çağırmak, import
İZİNLİ C tipleri, typed memoryview erişimi, cdef class C-tipli attribute'ları, cdef nogil fonksiyon çağrıları, libc fonksiyonları, printf
GİL GEREKİRSE with gil: bloğu kullanarak nogil bölge içinde geçici GIL geri al

prange ile paralel döngü

parallel.pyx — prange
from cython.parallel cimport prange

def parallel_compute(double[:] data, double[:] out):
    cdef int i, n = data.shape[0]
    # GIL olmadan OpenMP ile paralel döngü
    for i in prange(n, nogil=True, schedule='static'):
        out[i] = heavy_compute(data[i])

def parallel_sum(double[:] data) -> double:
    cdef int    i, n = data.shape[0]
    cdef double total = 0.0
    # Cython reduction'ı otomatik tespit eder
    for i in prange(n, nogil=True):
        total += data[i]
    return total
schedule='static'
İş parçaları eşit bölünür; tüm iterasyonlar benzer süre alıyorsa en iyi seçim
schedule='dynamic'
Her thread tamamlayınca yeni iş alır; iterasyon süreleri değişkense iyidir
schedule='guided'
Dynamic'e benzer ama parça boyutu küçülür — orta yol
num_threads=N
Kullanılacak thread sayısı; varsayılan: OMP_NUM_THREADS ortam değişkeni

setup.py — OpenMP desteği

setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize

ext = Extension(
    "parallel",
    sources=["parallel.pyx"],
    extra_compile_args=["-fopenmp"],
    extra_link_args=["-fopenmp"],
)
setup(ext_modules=cythonize([ext], language_level=3))

macOS: Apple Clang OpenMP desteği varsayılan kapalıdır. brew install libomp ve -Xclang -fopenmp -lomp flagleri gerekir. Windows: MSVC /openmp flag'i ile.

THREAD SAFETY

Paralel blok içinde aynı bellek konumuna farklı thread'lerden yazma data race'e yol açar. total += data[i] gibi reduction ifadelerini Cython otomatik güvenli hale getirir — ama özel bir pointer veya buffer'a iki thread aynı anda yazmamalı. Okuma her zaman güvenli.

09 Performans tuning: annotate + direktifler

Neyin yavaş olduğunu görmek — cython -a sarısını avla, direktiflerle söndür.

Annotate HTML

bash
cython -a matmul.pyx
# matmul.html dosyası üretildi
xdg-open matmul.html   # ya da tarayıcıda aç

Her satır bir renk alır:

Sarı (koyu) Bu satır Python C-API çağrısı içeriyor — potansiyel yavaşlık. Satıra tıklayınca üretilen C kodu görünür
Beyaz Saf C kodu — Python overhead yok

Sarı satır gördüğünde şunu sor: bu değişkene/argümana tip eklenebilir mi? Evet ise ekle, yeniden derle, rengine bak. Bu döngüyü tekrarla.

Compiler direktifleri

matmul_optimized.pyx
# cython: language_level=3
# cython: boundscheck=False
# cython: wraparound=False
# cython: cdivision=True
# cython: initializedcheck=False

def matmul_opt(double[:, ::1] A,
               double[:, ::1] B,
               double[:, ::1] C):
    cdef int i, j, k
    cdef int M = A.shape[0], K = A.shape[1], N = B.shape[1]
    for i in range(M):
        for j in range(N):
            for k in range(K):
                C[i, j] = C[i, j] + A[i, k] * B[k, j]
boundscheck=False
Dizi indeks sınır kontrolü kapat. a[100] boyutu 50 olan dizide segfault üretebilir. Profilleme doğrulandıktan sonra aç
wraparound=False
Negatif indeks (a[-1]) desteğini kapat. Döngüde kullanılmıyorsa güvenli
cdivision=True
Python'un sıfıra bölme ve işaret kurallarını kapat; C semantics kullan. -5 // 2 = -3 yerine -2
initializedcheck=False
Memoryview'ın geçerli bir buffer tutup tutmadığı kontrolünü kapat
nonecheck=False
cdef class değişkeninin None olup olmadığı kontrolünü kapat

Tek fonksiyon için dekoratör

cython
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def hot_loop(double[:] data):
    cdef int i, n = data.shape[0]
    for i in range(n):
        data[i] *= 2.0

Önce/Sonra benchmark (100×100 matmul)

Naive Cython (tip yok) ~4200 µs
Tipli, boundscheck açık ~180 µs — 23× hızlanma
+ boundscheck=False ~95 µs — ek 2×
+ wraparound=False, C-contig ~38 µs — ek 2.5×
+ prange (4 çekirdek) ~12 µs — ek ~3×

Profil desteği

cython
# cython: profile=True
# Derle, sonra cProfile ile çalıştır
bash
python -m cProfile -s cumtime myscript.py

profile=True her fonksiyon çağrısında overhead ekler. Sadece profil sırasında açın, production build'de kapatın.

ALTIN KURAL

Önce profille, sonra optimize et. Direktifleri körü körüne açmak segfault, yanlış hesaplama veya integer overflow getirebilir. Annotate HTML'iyle doğrula, timeit ile ölç, sonra direktif aç.

10 Build sistemleri ve dağıtım

Geliştirmeden PyPI'a — setup.py, pyproject.toml ve wheel üretimi.

Tam setup.py — tüm seçenekler

setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize

ext = Extension(
    name="mymod",
    sources=["mymod.pyx", "helper.c"],
    include_dirs=["./include"],
    library_dirs=["/usr/local/lib"],
    libraries=["m"],
    extra_compile_args=["-O3", "-fopenmp", "-march=native"],
    extra_link_args=["-fopenmp"],
)

setup(
    name="mymod",
    ext_modules=cythonize(
        [ext],
        language_level=3,
        annotate=True,            # setup sırasında da HTML üret
        compiler_directives={
            "boundscheck": False,
            "wraparound":  False,
        }
    )
)

Pyproject.toml (modern)

pyproject.toml
[build-system]
requires = ["setuptools>=64", "cython>=3.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mymod"
version = "0.1.0"
requires-python = ">=3.8"

pyproject.toml build-backend'i bildirir; Extension detayları hâlâ setup.py'da kalabilir ya da setup.cfg + setuptools.build_meta hook'larıyla pure-toml çözüme geçilebilir.

pyximport — geliştirme modu

python
import pyximport
pyximport.install(
    language_level=3,
    setup_args={"include_dirs": ["./include"]}
)
import mymod   # .pyx değiştiyse otomatik yeniden derlenir

Dağıtım: sdist ve wheel

bash
pip install build

# Source distribution (tarball)
python -m build --sdist
# dist/mymod-0.1.0.tar.gz içinde: .pyx + üretilmiş .c
# Kullanıcıda Cython yoksa .c'den derler — yeterlidir

# Binary wheel (platform-özel)
python -m build --wheel
# dist/mymod-0.1.0-cp311-cp311-linux_x86_64.whl
# İçinde: derlenmiş .so — kurulumda derleme gerekmez

Çoklu platform wheel: cibuildwheel

bash
pip install cibuildwheel

# Yerel: sadece mevcut platform
cibuildwheel --platform linux .

# CI'da (GitHub Actions): Linux × macOS × Windows × Python 3.8–3.12
.github/workflows/build.yml (özet)
- uses: pypa/cibuildwheel@v2
  with:
    package-dir: .
    output-dir: wheelhouse
  env:
    CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-*"

sdist içine .c dosyasını dahil etmek

MANIFEST.in
include *.pyx
include *.c
include *.h

.c dosyasını kaynak kontrolüne eklemek tartışmalıdır ama sdist kullanıcılarının Cython'a ihtiyaç duymaması için yaygın bir pratiktir.

11 Özet, karşılaştırma, köprü

Eğitim boyunca öğrendiklerinin kompakt formu.

def / cdef / cpdef karar ağacı

  Python'dan çağrılacak mı?
  │
  ├── Evet
  │   └── Cython içinden de hızlı çağrılsın mı?
  │       ├── Evet  →  cpdef
  │       └── Hayır →  def  (tip bildirimleri ekleyerek hızlandır)
  │
  └── Hayır (sadece iç hesaplama)
      └── cdef  (en hızlı, Python'dan görünmez)

Wire type tablosu (hızlı başvuru)

int / long / unsigned intTamsayı aritmetiği. long 64-bit Linux/macOS'ta en güvenli
double / float64-bit / 32-bit kayan nokta. NumPy float64/float32 ile birebir
bintBoolean — C int olarak saklanır, Python bool döner
size_tPlatform boyutu; malloc/sizeof argümanları için standart
T[:]1D typed memoryview — herhangi düzen
T[:, ::1]2D C-contiguous memoryview — NumPy default
T*Pointer — malloc/free ile birlikte, cdef class'ta __dealloc__'da free et

Direktifler özeti

boundscheck=FalseDizi sınır kontrolü kapat → segfault riski, büyük kazanç
wraparound=FalseNegatif indeks desteği kapat → küçük kazanç
cdivision=TrueC bölme semantics → Python'dan farklı işaret davranışı
initializedcheck=FalseMemoryview geçerlilik kontrolü kapat
profile=TruecProfile desteği ekle — sadece profil sırasında aç
language_level=3Her zaman açık olsun — Python 3 semantics zorunlu

protoc komut başvurusu (build)

python setup.py build_ext --inplaceModülü derle, yanına koy
cython -a file.pyxAnnotate HTML üret — sarı satırları avla
cython --versionCython sürümünü göster
python -m build --wheelDağıtılabilir binary wheel üret
python -m build --sdistKaynak dağıtımı üret (.pyx + .c dahil)

Alternatifler — ne zaman hangisi?

Cython Mevcut Python kodunu kademeli hızlandır; C kütüphanesi sar; NumPy post-processing; GIL release + paralel döngü. Build pipeline masrafını ödemeye hazırsan.
numba Pure Python + NumPy döngüsünü sıfır değişiklikle hızlandırmak. @jit dekoratörü yeter. Derleme zamanı ilk çağrıda olur.
ctypes Mevcut bir .so/.dll'i derleme olmadan çağırmak. Hızlı prototip için yeter.
cffi ctypes'tan daha temiz FFI; PyPy ile de çalışır. ABI ve API modu.
pybind11 C++ kütüphanesini Python'a aç. C++ yazıyorsan doğal seçim.
PyO3 Rust yazıyorsan ya da Rust öğrenmek istiyorsan. Bellek güvenliği ücretsiz.

Öğrendiklerimiz

  • .pyx → C → .so derleme zinciri; setup.py + cythonize()
  • def / cdef / cpdef farkı ve ne zaman hangisi
  • Struct, union, ctypedef, pointer ve malloc/free bellek yönetimi
  • cdef class: __cinit__, __dealloc__, typed attribute'lar
  • cdef extern from ile mevcut C fonksiyonları ve struct'ları sarmak
  • Typed memoryview (double[:, ::1]) ile NumPy'ya sıfır kopyalı erişim
  • nogil ve prange ile paralel döngü, OpenMP kurulumu
  • cython -a ile annotate HTML ve compiler direktifleri
  • sdist / wheel / cibuildwheel ile çoklu platform dağıtımı

Sonraki adım

Bu eğitim Cython'u sıfırdan kullanılabilir hale getirdi. Servisinize gRPC arayüzü eklemek istiyorsanız gRPC eğitimi'ne; veri formatı tanımlamak için Protocol Buffers eğitimi'ne geçebilirsiniz.