- Python’un işlem, bellek ve girdi/çıktı performans değerlerini sistematik olarak ölçen benchmark sonuçları; her işlemin süresi ve bellek kullanımını nicel olarak ortaya koyuyor
- Hız açısından öznitelik erişimi 14ns, listeye ekleme 29ns, dosya açma 9μs, FastAPI yanıtı 8.6μs gibi çeşitli işlemlerin göreli gecikmeleri sunuluyor
- Bellek açısından boş string 41 bayt, tamsayı 28 bayt, boş liste 56 bayt, boş sözlük 64 bayt, boş süreç 16MB gibi somut değerler veriliyor
- Veri yapıları, serileştirme, asenkron işleme gibi alanlarda standart kütüphane ile alternatif kütüphaneler (
orjson, msgspec vb.) arasındaki performans farkları karşılaştırılıyor
- Temel çıkarımlar olarak Python nesnelerinin yüksek bellek ek yükü,
dict/set’in hızlı sorgulaması, __slots__’un bellek tasarrufu etkisi ve asenkron işlemenin ek yükünü dikkate alma gereği vurgulanıyor
Genel bakış
- Python geliştiricilerinin bilmesi gereken performans göstergelerini derleyen bir çalışma; işlem hızı ve bellek kullanımını gerçek ölçümlerle sunuyor
- Benchmark’lar CPython 3.14.2, Mac Mini M4 Pro (ARM, 14 çekirdek, 24GB RAM) ortamında gerçekleştirildi
- Sonuçlar göreli karşılaştırmaya odaklanıyor; kod ve veriler GitHub deposunda açık olarak paylaşılıyor
Bellek kullanımı (Memory Costs)
- Boş bir Python süreci 15.73MB bellek kullanıyor
- String’ler temel olarak 41 bayt, karakter başına ek 1 bayt kullanıyor
- Örnek: boş string 41B, 100 karakterlik string 141B
- Sayısal türler: küçük tamsayılar (0–256) 28B, büyük tamsayılar (1000) da 28B, çok büyük tamsayılar (10ⁱ⁰⁰) 72B, kayan noktalı sayı 24B
- Koleksiyonların temel boyutu: liste 56B, sözlük 64B, küme 216B
- 1.000 öğede liste 35.2KB, sözlük 63.4KB, küme 59.6KB
- Sınıf örnekleri: normal sınıf (5 öznitelik) 694B,
__slots__ kullanan sınıf 212B
- 1.000 örnekte normal sınıf 165.2KB,
__slots__ sınıfı 79.1KB
Temel işlemler (Basic Operations)
- Aritmetik işlemler: tamsayı toplama 19ns, kayan noktalı toplama 18.4ns, tamsayı çarpma 19.4ns
- String işlemleri: birleştirme 39.1ns, f-string 64.9ns,
.format() 103ns, % biçimlendirme 89.8ns
- Liste işlemleri:
append() 28.7ns, liste üreteci/list comprehension (1.000 öğe) 9.45μs, aynı işlemin for döngüsü 11.9μs
- List comprehension, for döngüsünden yaklaşık %26 daha hızlı
Koleksiyon erişimi ve yineleme (Collection Access and Iteration)
- Anahtar/indeks erişimi: sözlük sorgulaması 21.9ns, kümede üyelik kontrolü 19ns, liste indeks erişimi 17.6ns
- Liste üyelik kontrolü (1.000 öğe) 3.85μs ile set/sözlükten yaklaşık 200 kat daha yavaş
- Uzunluk kontrolü:
len() listede 18.8ns, sözlükte 17.6ns, kümede 18ns
- Yineleme: liste (1.000 öğe) 7.87μs, sözlük 8.74μs,
sum() 1.87μs
Sınıflar ve öznitelikler (Class and Object Attributes)
- Öznitelik erişim hızı: hem normal sınıfta hem
__slots__ sınıfında okuma 14.1ns, yazma yaklaşık 16ns
- Diğer işlemler:
@property okuma 19ns, getattr() 13.8ns, hasattr() 23.8ns
__slots__ kullanıldığında bellek tasarrufu etkisi 2 katın üzerinde, erişim hızı ise benzer seviyede
JSON ve serileştirme (JSON and Serialization)
- Standart kütüphaneye kıyasla alternatif kütüphanelerin performansı
orjson, karmaşık nesnelerin serileştirilmesinde 310ns ile json’un 2.65μs değerinden 8 kattan fazla daha hızlı
msgspec 445ns, ujson ise 1.64μs
- Deserileştirme tarafında da
orjson 839ns ile en hızlısı
- Pydantic:
model_dump_json() 1.54μs, model_validate_json() 2.99μs
Web framework’leri (Web Frameworks)
- Aynı JSON yanıtı için FastAPI 8.63μs, Starlette 8.01μs, Litestar 8.19μs, Flask 16.5μs, Django 18.1μs
- FastAPI’nin yanıt süresi Django’dan yaklaşık 2 kat daha hızlı
Dosya girdi/çıktısı (File I/O)
- Dosya açma-kapama 9.05μs, 1KB okuma 10μs, 1MB okuma 33.6μs
- Yazma: 1KB 35.1μs, 1MB 207μs
- Pickle, hem serileştirme hem deserileştirmede
json’dan yaklaşık 2 kat hızlı (pickle.dumps() 1.3μs, json.dumps() 2.72μs)
Veritabanı ve kalıcılık (Database and Persistence)
- SQLite: insert 192μs, select 3.57μs, update 5.22μs
- diskcache: set 23.9μs, get 4.25μs
- MongoDB: insert 119μs, find_one 121μs
- Okuma hızında SQLite en hızlısı; yazma performansında ise diskcache öne çıkıyor
Fonksiyon çağrıları ve istisnalar (Function and Call Overhead)
- Fonksiyon çağrısı: boş fonksiyon 22.4ns, metot 23.3ns, lambda 19.7ns
- İstisna yönetimi: try/except (normal akış) 21.5ns, istisna oluştuğunda 139ns
- Tür kontrolü:
isinstance() 18.3ns, type() karşılaştırması 21.8ns
Asenkron ek yükü (Async Overhead)
- Coroutine oluşturma 47ns,
run_until_complete 27.6μs
asyncio.sleep(0) 39.4μs, gather(10 coroutines) 55μs
- Senkron fonksiyon çağrısına (20ns) kıyasla asenkron çalıştırma (28μs) yaklaşık 1.000 kat daha yavaş
Temel çıkarımlar (Key Takeaways)
- Python nesnelerinin bellek ek yükü büyüktür; boş bir liste bile 56 bayt kullanır
- Sözlük/küme sorgulamaları, liste taramasından yüzlerce kat daha hızlıdır
orjson, msgspec gibi alternatif JSON kütüphaneleri standart çözümlerden 3–8 kat daha hızlıdır
- Asenkron işleme yüksek ek yüke sahip olduğundan yalnızca paralellik gerektiğinde kullanılması önerilir
__slots__, performansta neredeyse kayıp olmadan belleği yarının altına indirebilir
1 yorum
Hacker News yorumları
Birçok kişi, “Python'da gecikme (latency) değerlerini dert etmeniz gerekiyorsa başka bir dil kullanmalısınız” diyor ama ben buna katılmıyorum
Instagram, Dropbox, OpenAI gibi büyük ölçekli kod tabanları da Python ile büyüdü. Sonunda performans sorunlarıyla karşılaşıyorsunuz ve başka bir dile taşımadan bunları Python içinde çözebilme becerisi önemli.
Performans sorunlarının çoğu dilin sınırlarından değil, verimsiz koddan kaynaklanıyor. Örneğin gereksiz yere fonksiyon çağrısını 10 bin kez tekrarlayan bir döngü gibi.
Hazırladığım Python latency quiz de faydalı olabilir
Paradoksal olarak, bu tür sayıların önemli hale geldiği anda Python o iş için uygun araç değildir
Pratikte önemli olan, kodu enstrümante edip (
pyspygibi araçlarla) darboğazları bulmak. Listeye eleman ekleme hızını dert edecek seviyedeyseniz, o işlemi Python'da yapmamalısınızPython ile C arasındaki birlikte çalışabilirlik sayesinde bu yaklaşım mümkün. Zig de giderek daha iyi hale geliyor. Python ile uçak kontrol etmem ama kaynak farkındalığı yine de önemli
Boş bir string'in kaç byte tuttuğunu bilmenin pek anlamı yok. Önemli olan zaman ve alan karmaşıklığını anlamak.
int'in 28 byte olduğunu bilmektense, programın performans gereksinimlerini karşılayıp karşılamadığını değerlendirmek ve karşılamıyorsa daha iyi bir algoritma bulmak daha önemliÖrneğin string birleştirmenin O(n²) olması, Python'daki f-string tasarımını da etkiler.
Sözlüklerin hızlı olması nedeniyle Python genelinde yaygın biçimde kullanılmaları da aynı bağlamda değerlendirilebilir.
Bu tür sayılar, o örtük bilgiyi sayısal olarak gerekçelendirme işlevi görüyor
int'in 28 byte olması, çok sayıda nesne üretmek gereken problemlerde gerçekten önemlidir.Eric Raymond'ın Reposurgeon ile GCC'yi migrate ederken yaşadığı sorunları anlattığı yazıyı hatırlatıyor
Başlık kafa karıştırıcı ama aslında Jeff Dean'in 2012 tarihli “Latency Numbers Every Programmer Should Know” makalesine bir gönderme.
Bu tür başlık oyunları CS makalelerinde yaygındır
Google'ın ilk dönem arama motorunda RAM vs Disk tasarımı için hazırlanmış dahili bir materyaldi.
Sonrasında flash belleklerin ortaya çıkmasıyla sayılar değişti ve Jeff'in genom verisini doğrudan flash üzerinden servis edebilmek için sıkıştırma algoritmaları geliştirdiğine dair bir anekdot da var
Python geliştiricilerinin çoğu, bu tür düşük seviye performans ayrıntılarından daha önemli işlere odaklanmalı.
Bu tür materyaller referans olarak iyi ama pratikte nadiren gerekli
String boyutu açıklaması hatalı. Python'da karakter başına 1, 2 veya 4 byte kullanan üç tür string tipi var
Ayrıntılar için bu bloga bakılabilir
Yazının başlığı ve örnekleri biraz isabetsiz.
Örneğin “
item in set,item in list'ten 200 kat daha hızlıdır” ifadesi membership test ile ilgili; iteration hızının karşılaştırılması değil.Buna rağmen genel olarak biçimi ve kurgusu çekici
Sınıf instance'ı oluşturma süresinin ölçümü eksik.
Ben kod refactor ettikten sonra basit bir liste yapısını sınıfa çevirdim ve çalışma süresi birkaç mikrosaniyeden birkaç saniyeye çıktı.
Böyle durumların da ölçülmesini isterdim
Sorun sınıfların aşırı kullanımı olabilir. Bazı durumlarda basit liste yapıları daha iyi olabilir
Daha çok nesne yönelimli yaklaşımın yanlış kullanılmış olma ihtimali vardır.
Kodu StackOverflow ya da CodeReview.SE'ye koyup geri bildirim almak iyi olabilir
Bu yazıyı, “modern Python'da temelde yanlış giden bir şey mi var” bakış açısından ilgi çekici buldum.
Ama bu sayıların “herkes tarafından bilinmesi gerektiği” iddiasına katılmıyorum.
Birkaç temel işlem için sezgisel bir fikir sahibi olmak yeterli
Python'daki small int cache aralığı 0~256 değil, -5~256'dır.
Bu yüzden özdeşlik (
is) ile eşitliği (==) karıştıran yeni başlayanlar sık sık kafası karışıyor