İçeriğe geç

Çok Ajanlı LLM Sistemlerinde GIL Darboğazı: Free-Threaded Python ve Pydantic ile Üretimde Gecikme Nasıl Düşürülür?

100 eşzamanlı tool call üzerinden GIL, asyncio, Python 3.14t, p95/p99 gecikme, Pydantic v2 strict sözleşmeleri ve C-extension risklerini üretim perspektifiyle inceliyoruz.

· TankDev Mühendislik

Saat 09.02’de yüz müşteri oturumu aynı anda bir araştırma ajanını çalıştırıyor. Her ana ajan; arama, belge okuma, fiyat kontrolü ve sonuç doğrulama için alt görevler açıyor. Dış API çağrıları beklerken sistem sakindir. Cevaplar aynı anda döndüğünde ise her sonuç regex ile temizleniyor, JSON ayrıştırılıyor, Pydantic sözleşmesinden geçiriliyor ve ortak durum deposuna yazılıyor. Grafana’da ağ bekleme süresi düşerken p99 yükseliyor. Sekiz çekirdekli makinede tek çekirdek yüzde 100’e dayanıyor, diğerleri yarı boş kalıyor. Bu, çok ajanlı LLM sistemlerinde sık gördüğümüz GIL imzasıdır.

Sorun ‘Python yavaş’ kadar basit değildir. LLM çağrısı çoğunlukla I/O-bound’dur ve asyncio burada doğru araçtır. Fakat model cevabı geldikten sonraki metin normalizasyonu, büyük JSON doğrulaması, Python regex döngüleri, şema dönüştürme, skor birleştirme ve bazı embedding ön/son işlemleri CPU zamanı tüketir. Klasik CPython’da aynı process içindeki Python thread’leri bu bölümlerde Global Interpreter Lock için sıraya girer. On ajanla görünmeyen birkaç milisaniyelik kuyruk, yüz tool call aynı anda tamamlandığında tail latency’ye dönüşür.

1. asyncio neden tek başına yetmez?

Bir event loop, bekleyen socket’i verimli yönetir; CPU çekirdeği üretmez. await client.post(...) sırasında kontrol başka coroutine’e geçer. Ancak cevabın içindeki 200 KB JSON’u Python koduyla tararken, yirmi regex kuralını uygularken veya yüzlerce alanı dönüştürürken coroutine gönüllü olarak beklemez. Event loop thread’i o iş bitene kadar diğer coroutine’leri ilerletemez. CPU işini asyncio.to_thread() ile thread pool’a taşımak loop’u rahatlatır, fakat GIL etkin CPython’da saf Python bölümleri gerçek anlamda paralel çalışmaz; yalnızca kilit için farklı thread’ler yarışır.

Birçok ekip ilk olarak thread sayısını artırır. Bu, bloklayan SDK çağrısı veya GIL’i C tarafında bırakan bir native işlem için faydalı olabilir. Saf Python regex/JSON dönüşümü baskınsa thread sayısı arttıkça context switch, queue wait ve bellek büyür. Ortalama süre kabul edilebilir görünürken p95 ve p99 bozulur. Çok ajanlı sistemlerde kullanıcı tek bir ortalamayı değil, kritik yol üzerindeki en yavaş alt görevi beklediği için kuyruk etkisi büyür.

text
request critical path
  ├─ planner: 35 ms CPU
  ├─ 12 tool calls: mostly network wait
  ├─ 12 response validations: 12 × 18 ms CPU
  ├─ merge + deduplication: 75 ms CPU
  └─ final contract validation: 24 ms CPU

CPU per request ≈ 350 ms
At 20 completed requests/s: 20 × 0.350 = 7 CPU-seconds/s
The workload needs roughly seven continuously busy cores before headroom.
One GIL-bound process cannot supply that Python-bytecode capacity.

Bu hesap bir concurrency ayarı değil, kapasite uyarısıdır. Sekiz vCPU’nun olması, tek bir GIL etkin process’in saf Python kodunda sekiz çekirdeği kullanacağı anlamına gelmez. Dört ayrı worker process açmak çekirdekleri kullanabilir; bunun bedeli dört heap, dört connection pool, dört model/şema cache’i ve süreçler arası koordinasyondur. Free-threaded CPython aynı process içinde thread paralelliği sunarak başka bir seçenek açar; process modelini otomatik olarak geçersiz kılmaz.

2. Free-threaded Python gerçekte neyi değiştirir?

CPython 3.13 ile GIL’siz, free-threaded derleme seçeneği geldi; Linux dağıtımlarında yorumlayıcı çoğunlukla python3.13t veya python3.14t adıyla ve cp313t/cp314t wheel etiketiyle görülür. Python 3.14’te specializing adaptive interpreter free-threaded modda da etkinleştirildi ve tek-thread ek yükü 3.13’e göre ciddi biçimde azaltıldı. Yine de bu ayrı bir runtime/ABI hedefidir: normal python3.14 binary’sine yalnızca bir ortam değişkeni vererek uyumsuz wheel’leri sihirli biçimde güvenli yapamayız.

Çalışan process’in gerçekten GIL’siz olduğunu startup probe ile kanıtlarız. sysconfig.get_config_var('Py_GIL_DISABLED') build’in free-threading desteğini, sys._is_gil_enabled() ise GIL’in o anda açık olup olmadığını gösterir. Çünkü free-threading desteğini ilan etmeyen bir C-extension import edildiğinde CPython güvenlik için GIL’i yeniden etkinleştirebilir. Container adı python:3.14t olduğu hâlde production’ın tekrar seri çalışması mümkündür.

python
import logging
import sys
import sysconfig

free_threaded_build = sysconfig.get_config_var("Py_GIL_DISABLED") == 1
gil_enabled = sys._is_gil_enabled() if hasattr(sys, "_is_gil_enabled") else True

logging.info(
    "python_runtime",
    extra={
        "version": sys.version,
        "free_threaded_build": free_threaded_build,
        "gil_enabled": gil_enabled,
    },
)

if not free_threaded_build or gil_enabled:
    raise RuntimeError("Expected a free-threaded runtime with the GIL disabled")

Free-threading, paylaşılan mutable state’i güvenli yapmaz. dict, list ve set gibi built-in tiplerde iç kilitler bazı tekil işlemleri korur; fakat ‘oku, karar ver, yaz’ dizisi atomik değildir. if job_id not in cache: cache[job_id] = build() iki thread’de iki kez çalışabilir. Aynı iterator’ı paylaşmak eksik veya çift öğe üretebilir. Sayaç, LRU cache, rate limiter ve agent scratchpad için açık lock, immutable snapshot, thread-local state veya mesaj geçişi gerekir.

3. 100 eşzamanlı tool call: sayısal kapasite modeli

Karşılaştırmayı belirsiz bırakmamak için istasyonu tanımlayalım: 8 vCPU x86-64 Linux, 16 GB RAM, tek application process, 32 worker thread, 100 in-flight tool sonucu. Her sonuç 64 KB JSON, 24 regex kuralı, alan normalizasyonu, Pydantic v2 doğrulaması ve küçük bir dedup hash’i içeriyor. Dış servis 80–140 ms arasında kontrollü gecikme üretiyor. Test on dakika ısınma, yirmi dakika ölçüm penceresiyle düşünülüyor. Aşağıdaki değerler bu yük için temsili kapasite hedefidir; yayınlanmış CPython benchmark sonucu değildir.

8 vCPU, tek process ve 100 eşzamanlı tool call için temsili sonuç
MetrikGIL etkin CPython 3.14Free-threaded CPython 3.14tMühendislik yorumu
Toplam process CPU%145%6103.14t Python thread’lerini birden çok çekirdeğe yayabilir
En yoğun çekirdek%98%88GIL etkin koşuda bir çekirdek seri bölümde tavana dayanır
Tamamlanan tool sonucu310/s790/s2,55× throughput; payload ve extension’a özeldir
p50 uçtan uca214 ms151 msI/O tabanı iki koşuda da kalır
p95 uçtan uca782 ms268 msKilit kuyruğu azaldığında tail toparlanır
p99 uçtan uca1.340 ms446 msAsıl kazanım ortalamadan çok kuyruk kuyruğundadır
RSS bellek735 MB948 MBThread metadata, allocator ve free-threading maliyeti büyür

Bu tabloda p99 yaklaşık yüzde 67 düşerken RSS yaklaşık yüzde 29 artıyor. Free-threading’i ‘ücretsiz 2,5×’ diye okumak yanlış olur. Saf I/O yükünde iki runtime birbirine yaklaşır; tek-thread işte 3.14t daha yavaş olabilir; yoğun lock paylaşımında ölçek çizgisel olmaz. Bir C-extension kendi içinde zaten GIL’i bırakıyorsa kazanç daha küçük olabilir. Uyumsuz extension GIL’i geri açarsa fark tamamen kaybolabilir.

python
# Benchmark harness sketch: record queue wait separately from service time.
async def station(payloads, executor, validate):
    loop = asyncio.get_running_loop()
    started = time.perf_counter_ns()
    results = await asyncio.gather(*[
        loop.run_in_executor(executor, validate, payload)
        for payload in payloads
    ])
    elapsed_ms = (time.perf_counter_ns() - started) / 1_000_000
    return elapsed_ms, results

# Report at minimum:
# runtime/build, GIL state, image digest, CPU model, core quota,
# package/wheel tags, payload histogram, GC stats, RSS, p50/p95/p99,
# throughput, errors, and per-stage queue_wait_ms/service_ms.

Ölçümde yalnız HTTP latency’sine bakmayız. Thread pool queue wait, validation service time, event-loop lag, process CPU, çekirdek dağılımı, RSS, GC pause ve schema error oranını ayrı histogramlarız. Kubernetes’te node’daki 8 çekirdeği değil pod’un CPU quota’sını esas alırız. Test imajını digest ile sabitler, aynı payload corpus’unu iki runtime’da çalıştırırız. Aksi hâlde Python sürümü ile trafik dağılımını aynı anda değiştirmiş oluruz.

4. Pydantic v2 ile LLM çıktısını sözleşmeye kapatmak

Free-threading throughput’u artırabilir; yanlış çıktıyı doğru yapmaz. LLM’den gelen metin bir komut değil, güvenilmeyen girdidir. Tool çağrısının parametreleri önce JSON sınırında parse edilmeli, sonra katı ve sürümlü bir şemaya uymalıdır. Pydantic v2’de model_validate_json JSON’u doğrudan pydantic-core doğrulama yoluna verir. ConfigDict(strict=True, extra='forbid') ile "42" değerinin integer’a sessizce çevrilmesini ve şemada olmayan alanların içeri sızmasını engelleriz.

python
from typing import Annotated, Literal
from pydantic import BaseModel, ConfigDict, Field, ValidationError

class SearchToolArgs(BaseModel):
    model_config = ConfigDict(strict=True, extra="forbid")

    schema_version: Literal[1]
    query: Annotated[str, Field(min_length=3, max_length=500)]
    top_k: Annotated[int, Field(ge=1, le=20)]
    locale: Literal["tr-TR", "en-US"]
    include_domains: list[str] = Field(default_factory=list, max_length=10)

def validate_tool_call(raw_json: str) -> SearchToolArgs:
    if len(raw_json.encode("utf-8")) > 16_384:
        raise ValueError("tool arguments exceed 16 KiB")
    return SearchToolArgs.model_validate_json(raw_json, strict=True)

try:
    args = validate_tool_call(model_output)
except (ValidationError, ValueError) as exc:
    # Persist a bounded error code and field paths, not the full sensitive prompt.
    reject_locally(error_code="TOOL_CONTRACT_INVALID", details=str(exc)[:1000])

Strict mode, JSON ile Python nesnesi girişinde bazı tipler için farklı davranabilir; bu nedenle production’daki gerçek giriş yolunu benchmark ederiz. Şema yalnız tipleri değil sınırları da taşır: string uzunluğu, liste elemanı, enum, discriminator ve bilinmeyen alan politikası. URL, dosya yolu, SQL parçası veya para tutarı için ayrıca iş kuralı gerekir. str tipinde olmak bir URL’nin güvenli veya bir hesabın yetkili olduğu anlamına gelmez.

Fast-fail: yeniden prompt atmadan önce yerelde karar verin

Her ValidationError için modele ‘bir daha dene’ demek token ve gecikme bütçesini tüketir. Önce hatayı sınıflandırırız. JSON hiç kapanmamışsa veya zorunlu alan yoksa, aynı modele yalnızca küçük ve şemaya odaklı tek bir repair attempt düşünülebilir. top_k=5000, izin verilmeyen domain veya bilinmeyen tool adı gibi politika ihlalleri yerelde terminal reddedilir. Kullanıcının yetkisi olmayan tenant kimliği hiçbir repair prompt’una girmez. Aynı agent adımında en fazla bir düzeltme, tüm workflow’da ayrı bir repair bütçesi tutarız.

Şema hatasında token harcamadan karar tablosu
HataYerel kararLLM tekrar çağrısı
Payload > 16 KiBHemen reddet, metrik artırHayır
Bilinmeyen tool / schema_versionUyumluluk veya güvenlik hatasıHayır
Ek alan (extra_forbidden)Reddet; prompt drift sinyaliGenellikle hayır
Eksik zorunlu alanKüçük hata özeti üretEn fazla bir bounded repair
Yetkisiz domain/tenantPolicy deny + auditKesinlikle hayır
Geçici tool 429/5xxDurable retry/backoffŞemayı yeniden üretme
Doğrulama hatası, modelin daha çok konuşması gereken bir an değil; sistemin daha kesin karar vermesi gereken bir andır.

5. Üretim mimarisi: async I/O, bounded thread pool ve process izolasyonu

Doğru mimari tüm işi thread’e atmaz. Network I/O event loop’ta kalır. Kısa ve ölçülebilir CPU aşamaları bounded executor’a gider. Global thread sayısı tool sayısından değil CPU bütçesi ve ölçülen service time’dan çıkarılır. Büyük embedding modeli, thread-safe olmadığı doğrulanmamış tokenizer veya uzun süren native çağrı ayrı process/service sınırına alınabilir. Agent başına ayrı executor açmak yüz ajanla yüz havuz üretir; tek process bütçesi altında paylaşılan, öncelikli ve backpressure uygulayan kuyruk tercih ederiz.

python
from concurrent.futures import ThreadPoolExecutor
import asyncio

CPU_WORKERS = 8          # begin near the effective CPU quota, then measure
MAX_PENDING = 64         # admission bound, not an infinite executor queue
executor = ThreadPoolExecutor(max_workers=CPU_WORKERS, thread_name_prefix="contract")
pending = asyncio.Semaphore(MAX_PENDING)

async def validate_bounded(raw_json: str) -> SearchToolArgs:
    async with pending:
        loop = asyncio.get_running_loop()
        return await loop.run_in_executor(executor, validate_tool_call, raw_json)

# Network awaits stay async. Validation is bounded. Cancellation, deadlines,
# context propagation and shutdown are tested explicitly in both runtimes.

Bir deployment için üç makul profil vardır. I/O’nun ağır, CPU’nun düşük olduğu serviste normal CPython + asyncio en sade seçenektir. CPU-bound Python aşamaları yüksek ama extension matrisi belirsizse çok-process worker güvenli geçiş yoludur. Wheel’ler ve yarış testleri uygunsa 3.14t ile daha az process, daha çok thread denenebilir. Hibrit model de mümkündür: pod başına iki process, process başına dört-sekiz thread. Seçim sloganla değil p99, RSS ve hata oranıyla yapılır.

6. GIL’siz production’da C-extension ve race condition riskleri

GIL yıllarca yalnız interpreter kilidi değil, birçok extension’ın farkında olmadan dayandığı global mutex oldu. Module-level cache, lazy singleton, statik buffer, reference count varsayımı veya GIL altında çalışan callback serbest thread ortamında yarışabilir. Extension free-threading desteğini bildirmezse CPython GIL’i yeniden açabilir; yanlış biçimde destek bildiren bir extension ise daha tehlikelidir, çünkü benchmark hızlı görünürken nondeterministic crash veya veri bozulması üretir.

Kütüphane güvenliğini isimle değil davranış ve build kanıtıyla sınıflandırın
Sınıf / örnekBaşlangıç varsayımıProduction kapısı
Saf Python immutable yardımcılarGenelde uygunPaylaşılan state yok; race testleri temiz
Pydantic v2 / pydantic-coreSürüm ve wheel’e bağlıcp314t wheel, upstream desteği, paralel validation soak testi
NumPy / SciPy / tokenizer / crypto native koduFonksiyon ve sürüme bağlıFree-threaded wheel + aynı nesnenin paylaşım kuralları + sanitizer/soak
HTTP/DB client pool’larıClient sözleşmesine bağlıDokümante thread safety; transaction/cursor thread’ler arasında paylaşılmaz
Eski veya kapalı kaynak C-extensionRiskliİzole process; destek kanıtı yoksa ana 3.14t process’e alma
Kendi pybind11/Cython/C-API modülünüzRiskli kabul etGlobal state lock, free-thread init bildirimi, TSan/ASan ve fault injection

‘NumPy thread-safe’ veya ‘Pydantic güvenli’ gibi tek cümleler production kararı için yetersizdir. Aynı paket içinde bazı fonksiyonlar GIL’i bırakabilir, bazı nesneler mutation sırasında paylaşılmamalıdır. Wheel’in cp314t olması gerekli ama tek başına yeterli değildir. Kilitsiz paylaşılan model nesnesi, callback ve allocator davranışı ayrıca test edilir. Güncel destek matrisi release sırasında tekrar kontrol edilir; bugünkü listeyi kalıcı gerçek gibi kod deposuna gömmeyiz.

  • İmajı python -VV, Py_GIL_DISABLED, sys._is_gil_enabled() ve yüklü wheel etiketleriyle envanterleyin.
  • Her import grubundan sonra GIL durumunu yeniden ölçün; sessiz performans gerilemesini deploy kapısına bağlayın.
  • Aynı seed ve payload corpus’uyla 6–24 saatlik concurrency soak testi çalıştırın.
  • Crash, deadlock, veri farkı, duplicate side effect ve RSS eğimini ayrı alarm yapın.
  • Thread sanitizer destekli native testleri ve debug allocator koşularını CI’da tutun.
  • Sorunlu extension için tüm servisi geri almak yerine process izolasyonu ve hızlı feature flag hazırlayın.

7. Paylaşılan agent state: görünmeyen yarışların başladığı yer

Çok ajanlı sistemlerde en tehlikeli yarış çoğu zaman tokenizer’da değil orchestration state’indedir. İki sub-agent aynı conversation scratchpad’ini okur, ikisi de yeni mesaj ekler ve son yazan öncekinin güncellemesini kaybeder. Bir tool sonucu iki callback tarafından ‘completed’ sayılabilir. GIL etkinken bile bu protokol doğru değildir; free-threading yalnız hatanın ortaya çıkma olasılığını büyütür.

python
# Prefer versioned state transitions over a shared mutable dict.
UPDATE agent_steps
SET state = 'SUCCEEDED', result_ref = :result, version = version + 1
WHERE id = :step_id
  AND state = 'RUNNING'
  AND version = :expected_version
RETURNING version;

# Zero rows means ownership/version was lost. Do not publish the result.
# External side effects also require an idempotency key and durable outbox.

Thread lock process içindeki belleği korur; pod crash’ini veya ikinci replica’yı kapsamaz. Kalıcı agent adımlarında optimistic version, lease/fencing, idempotency key ve outbox gerekir. Free-threaded Python throughput katmanıdır; dağıtık doğruluk protokolünün yerine geçmez.

8. Geçiş planı: production’ı tek gecede 3.14t’ye taşımayın

  1. Önce profilleyin: py-spy/perf, event-loop lag ve aşama histogramlarıyla CPU’nun gerçekten Python validation/regex bölümünde olduğunu kanıtlayın.
  2. Tekrarlanabilir corpus kurun: gerçek veriyi anonimleştirin; boyut, dil, hata ve tool dağılımını koruyun.
  3. Bağımlılık SBOM’u çıkarın: tüm native wheel’leri, transitif extension’ları ve t ABI uygunluğunu listeleyin.
  4. Normal CPython’da process baseline’ı alın; 3.14t sonucunu yalnız mevcut üretim alternatifiyle karşılaştırın.
  5. Shadow traffic’te sonuç eşitliği, exception sınıfı ve side-effect sayısını karşılaştırın.
  6. Yüzde 1 canary ile başlayın; p99, RSS, GIL state, crash ve schema reject oranına otomatik rollback bağlayın.
  7. Canary büyürken thread sayısını sabit tutun; runtime ve concurrency ayarını aynı deneyde değiştirmeyin.

Başarı kriterini ‘CPU yükseldi’ diye tanımlamayız. Daha fazla çekirdeğin kullanılması amaç değil araçtır. Örnek kabul kapısı: aynı doğru sonuç oranında p95 en az yüzde 30 düşük, p99 en az yüzde 40 düşük, RSS artışı yüzde 35’in altında, crash/deadlock sıfır, GIL beklenmedik biçimde açılmamış ve tool side-effect sayısı baseline ile eşit. Bu kapı geçilmiyorsa process modeli daha iyi mühendislik kararı olabilir.

9. Free-threaded Python ne zaman doğru yatırımdır?

Free-threading kararı, ajan sayısından değil profil sonucundan çıkar. Bir isteğin önemli bölümü model veya tool ağı beklemekse GIL ana darboğaz değildir; önce bağlantı havuzu, timeout, backpressure ve sağlayıcı kotası düzeltilir. Darboğaz saf Python regex, ayrıştırma, skor birleştirme veya şema doğrulamasıysa ve bu işler aynı anda yığılıyorsa 3.14t anlamlı bir adaydır. Embedding çağrısı uzaktaki bir API’ye gidiyorsa yine I/O-bound’dur; yerel embedding ise ancak tokenizer ve model runtime’ının hangi bölümlerde GIL’i bıraktığı ölçüldükten sonra bu sınıfa konur.

Ölçülen darboğaza göre ilk teknik hamle
Üretim sinyaliMuhtemel darboğazİlk tercih
Event-loop lag düşük, tool süresi yüksekHaricî I/O veya sağlayıcıasyncio, connection pool, timeout ve kota yönetimi
Bir çekirdek dolu, executor queue wait yüksekGIL altında saf Python CPU işi3.14t deneyi veya çok-process worker
Tüm çekirdekler doluGerçek CPU kapasitesiİşi azaltma, batch, algoritma veya yatay ölçekleme
CPU düşük, p99 ve queue depth büyüyorAdmission/backpressure veya downstream kuyrukBounded queue, tenant kotası ve yük atma
3.14t sonrası RSS hızla büyüyorBellek/allocator baskısıDaha az thread, process sınırı veya klasik CPython’a dönüş
Extension importundan sonra tek çekirdek doluyorGIL yeniden etkinleşmiş olabilirRuntime probe, uyumlu wheel veya process izolasyonu

Kararı istek başına CPU bütçesiyle sayısallaştırırız. Örneğin doğrulama ve birleştirme toplamı 280 ms CPU tüketiyor, hedef throughput 25 istek/s ise sistem saniyede 7 CPU-saniyesi ister. Tek GIL etkin process bu ihtiyacı karşılayamaz. Seçenekler; yaklaşık yedi çekirdeklik process paralelliği, doğrulanmış bir 3.14t thread modeli veya CPU işini ayrı bir servise taşımaktır. Hangisinin doğru olduğu yalnız latency ile değil RSS, bağlantı sayısı, cache tekrarı, crash izolasyonu ve işletim maliyetiyle birlikte ölçülür.

Sonuç: GIL’i kapatmak değil, kuyruğu ve sözleşmeyi yönetmek

Çok ajanlı LLM sisteminde gecikme tek bir model çağrısından doğmaz. Aynı anda dönen tool cevapları, CPU-bound doğrulama, paylaşılan state ve sınırsız executor kuyruğu tail latency’yi üretir. Free-threaded CPython 3.14t, uygun workload’da aynı process’in çekirdekleri kullanmasını sağlayabilir. Pydantic v2 ise hız kazanımını güvenilir tool sözleşmesine çevirir: strict schema, boyut sınırı, fast-fail ve kontrollü repair bütçesi.

Üretimde doğru sıra şudur: önce profille, sonra sözleşmeyi sıkılaştır, kuyruğu sınırla, extension matrisini kanıtla, iki runtime’ı aynı corpus’ta ölç ve canary ile ilerle. GIL’siz çalışma bir mimari özellik değil, doğrulanması gereken bir deployment seçeneğidir. LLM worker’larını kuyrukla ölçekleme rehberimizi ve çok ajanlı mimari sınırlarını birlikte okuyarak yürütme ile güvenilirlik katmanlarını tamamlayabilirsiniz.

Sık sorulan sorular

01GIL nedir ve çok ajanlı LLM sistemini neden etkiler?

GIL, klasik CPython’da aynı process içinde aynı anda yalnız bir thread’in Python bytecode yürütmesini sağlayan kilittir. LLM ağ beklemesinde etkisi sınırlıdır; aynı anda dönen cevapların regex, JSON dönüştürme ve doğrulama aşamasında thread’leri seri hâle getirip p95/p99 gecikmesini büyütebilir.

02asyncio GIL darboğazını çözer mi?

Hayır. asyncio çok sayıda bekleyen I/O işlemini verimli yönetir. CPU-bound Python kodu event loop’u bloklayabilir; thread pool’a taşındığında da GIL etkin runtime’da saf Python bölümleri paralel yürümeyebilir.

03Free-threaded Python hangi sürümde var?

Free-threaded CPython 3.13’te deneysel seçenek olarak geldi ve 3.14’te performans ve uygulama olgunluğu arttı. Dağıtıma göre binary python3.13t veya python3.14t, wheel etiketi ise cp313t veya cp314t olabilir.

04Process’te GIL’in gerçekten kapalı olduğunu nasıl anlarım?

Build desteğini sysconfig.get_config_var('Py_GIL_DISABLED'), çalışma anındaki durumu desteklenen sürümlerde sys._is_gil_enabled() ile kontrol edin. Startup’ta ve kritik extension import’larından sonra bu değeri loglayıp deploy kapısı yapın.

05Free-threaded Python her LLM uygulamasını hızlandırır mı?

Hayır. Saf I/O yükünde kazanç düşük olabilir; tek-thread işte ek maliyet görülebilir. Kazanç, aynı process’te paralelleşebilen CPU-bound Python işi varsa ve paylaşılan lock/extension sınırları ölçeklenmeyi engellemiyorsa ortaya çıkar.

06Pydantic v2’de LLM JSON’u için önerilen giriş yolu nedir?

Boyutu sınırlandırılmış ham JSON’u sürümlü bir modelde model_validate_json(..., strict=True) ile doğrulayın. Modelde ConfigDict(strict=True, extra='forbid'), alan limitleri, enum ve discriminator kullanın; yetki ve iş kurallarını ayrıca kontrol edin.

07Strict mode neden önemlidir?

Strict mode, modelin yanlış tiplerini sessiz coercion ile kabul etme riskini azaltır. Örneğin integer beklenen yerde gelen string’i otomatik dönüştürmek yerine sözleşme hatası üretir; böylece tool sınırı öngörülebilir kalır.

08Her şema hatasında LLM’e tekrar sormalı mıyım?

Hayır. Boyut aşımı, bilinmeyen tool, yetki ihlali veya yasak alan yerelde reddedilmelidir. Yalnız onarılabilir eksik alanlarda, küçük hata özetiyle en fazla bir bounded repair attempt düşünülebilir.

09Hangi Python kütüphaneleri free-threading için güvenlidir?

Kalıcı ve evrensel bir isim listesi yoktur. Tam sürümün free-threaded wheel’i, upstream desteği, paylaşılan nesne sözleşmesi ve kendi paralel soak testiniz birlikte değerlendirilmelidir. Eski veya kapalı kaynak extension’ı destek kanıtı olmadan riskli kabul edin.

10C-extension GIL’i yeniden açabilir mi?

Evet. Free-threading desteğini bildirmeyen extension import edildiğinde CPython GIL’i yeniden etkinleştirebilir ve uyarı üretebilir. Bu yüzden container etiketine değil runtime probe’a güvenin.

11Thread mi process mi seçmeliyim?

I/O ağırlıklı serviste asyncio, uyumsuz native bağımlılıkta veya güçlü izolasyon ihtiyacında process modeli, doğrulanmış CPU-bound Python yükünde ise 3.14t thread modeli uygundur. Hibrit model de p99 ve RSS ölçümüne göre seçilebilir.

12Production geçişinde en kritik metrikler hangileri?

p50/p95/p99, throughput, executor queue wait, event-loop lag, çekirdek dağılımı, RSS, GC pause, GIL state, crash/deadlock, schema reject oranı ve duplicate side-effect sayısını birlikte izleyin.

İlgili notlar

WhatsAppDoğrudan iletişim