Milliman’ın finansal risk yönetimi uygulamasında Principal olan Chad Schuster, tamamen C++ ile yazılmış uygulamalara güvenmek yerine Python ve Numba kullanarak yüksek performanslı hesaplama modelleri oluşturma konusunda pratik bir deneyim sundu. Deney, finansal hizmetlerde yaygın bir sorundan yola çıkıyor: Çok sayıda senaryo üzerinden nakit akışlarını simüle eden modeller önemli bir hesaplama gücüne ihtiyaç duyarken ekipler Python’ın geliştirme hızını ve bakım kolaylığını korumak istiyor.
Bu sorun, gelecekteki yükümlülükleri değerlendirmek ve birden fazla senaryoyu çalıştırmak için hesaplamaların kullanıldığı hayat sigortası modelleri ve annuities gibi emeklilik ürünlerinde özellikle önem kazanıyor. Sunuma göre sigorta şirketleri tarihsel olarak 5.000 ile 10.000 düğümden oluşan yerel ağlara güveniyordu; ancak buluta geçiş, çalışma süresini ve her işlemin maliyetini altyapı kararlarında daha görünür hâle getirdi.
Numba Python’a ne katıyor?
Geleneksel Python veya CPython, kodu çalışma sırasında yorumlayarak yürütürken C++ gibi derlenen diller kodu çalıştırılmadan önce makine talimatlarına dönüştürür. Numba, Just-in-Time derleme aracılığıyla bu farkı azaltmaya çalışır: Uygun işlevler program çalışırken LLVM kullanılarak derlenir ve ardından özgün işlevin yerine derlenmiş uygulama geçirilir.
Geliştirici bu süreci genellikle JIT veya njit gibi Python dekoratörleri aracılığıyla etkinleştirir. Numba işlev düzeyinde çalışır; kodu inceler ve kendi özel ara gösterimine dönüştürür, ardından değişkenlerin, parametrelerin ve dönüş değerlerinin türlerini çıkarır, son olarak gösterimi LLVM IR’ye indirger ve gerekli optimizasyonları gerçekleştirir. İşlev farklı türlerle çağrılırsa Numba, polymorphic dispatch olarak adlandırdığı yöntemle her tür kümesi için özelleştirilmiş derlenmiş uygulamalar oluşturabilir.
Performans kazanımları sabit bir sayı değildir
Schuster’ın sunduğu kavram kanıtında Numba, programı yorumlanan Python’a kıyasla yaklaşık 75 kat hızlandırdı. Başka bir modelde sonuç farklıydı: Yoğun sayısal hesaplamaların Numba ile merkezi işlemciye taşınması yaklaşık iki kat hızlanma sağladı; ardından GPU’ya geçiş, o belirli çalıştırmada 750 katlık ek bir hızlanma sağladı. Sunumda, bu iyileşme düzeyinin GPU birimini söz konusu çalıştırmada kullanılan yaklaşık 750 çekirdeğe eşdeğer hâle getirdiği ve tahmini maliyeti yaklaşık onda bire indirdiği belirtildi.
Ancak bu sonuçlar her uygulama için genel bir vaat oluşturmuyor. İyileşme; derlenebilir kod miktarına, hesaplama işlemlerinin girdi-çıktı işlemlerine oranına, özgün Python’ın verimliliğine ve LLVM’nin üretilen kodu optimize etme kapasitesine bağlıdır. GPU’da işlem gerçekleştirmek de her algoritma için uygun değildir; bu nedenle model yeniden tasarlanmadan önce gerçek darboğaz ölçülmelidir.
Mühendislik ekipleri açısından pratikte ne değişiyor?
Deney, veri hazırlama ile girdi ve çıktı katmanlarının Python’da tutulmasını ve mümkün olduğunda yalnızca yoğun sayısal bölümün Numba’ya taşınmasını öneriyor. Bu yaklaşım yeniden yazım kapsamını azaltır ve Python ortamının büyük bir bölümünü korurken optimizasyon çabasını çalışma süresinin çoğunu tüketen işlevlere odaklar. Sayısal işlevler sınırlı ve net olduğunda Schuster, Python ekosisteminde kalmak isteyenler için Numba’yı doğrudan bir seçenek olarak görüyor.
Buna karşılık kapsamlı ve karmaşık mantığın Numba’ya taşınmasıyla tasarım ve bakım maliyeti artıyor. Deneyde Numba desteği Python’ın tüm özelliklerini kapsamadığı için NumPy dizileri, tuple’lar ve basit veri yapıları kullanıldı. Belirtilen sınırlamalar arasında esnek türlere sahip sözlükler, istisnalar, context manager’lar, closure’lar ve comprehension’larla oluşturulan listelerin yanı sıra print, sorted ve getattr gibi bazı yaygın işlevlerin sınırlı desteği bulunuyor.
Bağımlı hâle gelmeden önce hesaba katılması gereken sınırlamalar
Nesne yönelimli programlama bu deneyde önemli bir zayıf nokta olmaya devam ediyor. Numba, nesne benzeri davranış sağlamak için jitclasses ve structrefs gibi deneysel özellikler sunuyor; ancak bunlar sürümler arasında değişebilir ve GPU’yu desteklemez. Bu durum, söz konusu özellikleri ekibin benimsediği yaklaşım için uygunsuz hâle getirdi. Bu nedenle uygulama, bakım kolaylığı ve modellerin müşterilere teslim edilmesi açısından nesne yönelimli tasarım arzu edilse de işlevsel programlamaya ve basit veri yapılarına daha yakın bir yönteme yöneldi.
Tür çıkarımı hataları ve lowering hataları da özellikle çok sayıda çağrı katmanına sahip programlarda izlenmesi zor olabilir. Hata, sorunun gerçekte ortaya çıktığı konumdan uzaktaki bir işlevi gösterebilir. Önerilen pratik yöntemlerden biri, gerektiğinde njit yerine JIT kullanmaktır; böylece hata ayıklamayı kolaylaştırmak için Numba geçici olarak devre dışı bırakılıp yorumlanan Python’a dönülebilir, ardından üretim çalıştırmasında hızlandırma yeniden etkinleştirilebilir.
Her işlevin ilk çağrısında ortaya çıkan bir ilk derleme süresi de vardır ve inlining tekniği geniş ölçekte kullanıldığında bu süre büyüyebilir. Bu gecikmeyi önlemek için Ahead-of-Time derlemeye başvurulabilir; ancak bu yöntem, Just-in-Time derlemeye kıyasla kodun gerçek cihaza uyum sağlama kapasitesini azaltabilir.
Editoryal değerlendirme: Bu deneydeki temel değer, 750 kat sayısının kendisinde değil, performans ölçümünü modelin bölümlere ayrılmasıyla ilişkilendiren yöntemdedir. Numba, darboğaz hesaplama kaynaklı olduğunda ve açık türlere ve yapılara sahip işlevlerde izole edilebildiğinde uygundur; ancak bütün bir Python sistemini derlenebilir koda dönüştürmek, sorunu yürütme yavaşlığından geliştirme ve hata ayıklama karmaşıklığına taşıyabilir. Bu nedenle finans ve mühendislik ekipleri, C++ için kapsamlı bir alternatif veya sistemin daha geniş kapsamlı yeniden tasarlanması olarak değerlendirmeden önce çalışma hızı, bakım kolaylığı, derleme süresi ve GPU uyumluluğu arasında denge kurmalıdır.