İşte nedeni fonksiyonel programlama için benim argümanlar vardır can ve gereken hesaplamalı bilim için kullanılabilir. Yararları çok büyük ve eksileri hızla gidiyor. Aklımda sadece bir con var:
Con : C / C ++ / Fortran'da dil desteğinin olmaması
En azından C ++ 'da bu kural ortadan kalkıyor - C ++ 14/17, fonksiyonel programlamayı desteklemek için güçlü olanaklar ekledi. Kütüphane / destek kodunu kendiniz yazmanız gerekebilir, ancak dil arkadaşınız olacaktır. Örnek olarak, burada C ++ 'da değişmez çok boyutlu dizileri olan bir (uyarı: fiş) kütüphanesi: https://github.com/jzrake/ndarray-v2 .
Ayrıca, burada fen bilimleri uygulamalarına odaklanmamasına rağmen, C ++ 'da fonksiyonel programlama üzerine iyi bir kitap .
İşte profesyonellerin olduğuna inandığım şeyin özeti:
Artıları :
- doğruluk
- Anlaşılırlık
- Verim
Doğruluk açısından , işlevsel programlar açıkça ortaya konur : sizi fizik değişkenlerinizin asgari durumunu ve zaman içinde ilerleyen işlevi doğru şekilde tanımlamanız için zorlarlar:
int main()
{
auto state = initial_condition();
while (should_continue(state))
{
state = advance(state);
side_effects(state);
}
return 0;
}
Kısmi bir diferansiyel denklemin (veya ODE) çözülmesi, fonksiyonel programlama için mükemmeldir; sadece bir advance
sonrakini üretmek için mevcut çözüme saf bir işlev ( ) uygulıyorsunuz.
Tecrübelerime göre fizik simülasyon yazılımı kötü durum yönetimi tarafından yüklenen büyük ve büyüktür . Genellikle, algoritmanın her aşaması, paylaşılan (etkin bir şekilde küresel) durumun bir parçası üzerinde çalışır. Bu, doğru işlem sırasını sağlamayı zorlaştırır, hatta imkansız hale getirir, yazılımı seg-hatalar olarak tezahür ettirebilecek hatalara veya kodunuzu çökertmeyen ancak bilimin bütünlüğünü sessizce tehlikeye sokan hata terimlerine karşı savunmasız bırakır. çıktı. Bir fizik simülasyonunda paylaşılan durumu yönetmeye çalışmak aynı zamanda çoklu iş parçacılığını da engelliyor - bu, süper bilgisayarlar daha yüksek çekirdek sayımlarına doğru ilerlerken ve MPI ile ölçeklendirmenin çoğu zaman ~ 100k görevde üst sıralara çıkması nedeniyle gelecek için bir sorun. Buna karşılık, işlevsel programlama, değişmezlik nedeniyle paylaşılan bellek paralelliği önemsiz kılmaktadır.
Performans , algoritmaların tembel değerlendirmesi nedeniyle fonksiyonel programlamada da geliştirilmiştir (C ++ 'da, derleme zamanında birçok tip - yani bir fonksiyonun her uygulaması için bir tane). Ancak, sanal gönderimi ortadan kaldırmanın yanı sıra, bellek erişimlerinin ve tahsislerinin ek yükünü azaltır - derleyicinin bir bütün algoritmayı, onu oluşturan tüm fonksiyon nesnelerini bir kerede görerek optimize etmesine izin verir. Uygulamada, CPU ve bellek tahsislerinin kullanımını optimize etmek için değerlendirme noktalarının (algoritma sonucunun bir bellek tamponuna önbelleğe alındığı) farklı düzenlemelerini deneyeceksiniz. Modül veya sınıf tabanlı bir kodda göreceğinize kıyasla, algoritma aşamalarının yüksek yerelliği (aşağıdaki örneğe bakın) nedeniyle bu oldukça kolaydır.
Fonksiyonel programları , fizik durumunu önemsizleştirdikleri sürece anlamak daha kolaydır . Bu onların sözdizimlerinin tüm meslektaşlarınız tarafından kolayca anlaşılabilir olduğunu söylemek değildir! Yazarlar, iyi adlandırılmış işlevleri kullanmaya dikkat etmeli ve genel olarak araştırmacılar, işlemsel olarak değil, işlevsel olarak ifade edilen algoritmaları görmeye alışmalıdırlar. Kontrol yapılarının yokluğunun bazılarına zarar verebileceğini itiraf edeceğim, ancak bunun bilgisayarlarda daha iyi kalitede bilim yapabilmek için geleceğe girmemizi engellemesi gerektiğini düşünmüyorum.
Aşağıda paket advance
kullanılarak sınırlı hacimli bir koddan uyarlanmış bir örnek fonksiyon verilmiştir ndarray-v2
. to_shared
Operatörleri not edin - bunlar daha önce bahsettiğim değerlendirme noktaları.
auto advance(const solution_state_t& state)
{
auto dt = determine_time_step_size(state);
auto du = state.u
| divide(state.vertices | volume_from_vertices)
| nd::map(recover_primitive)
| extrapolate_boundary_on_axis(0)
| nd::to_shared()
| compute_intercell_flux(0)
| nd::to_shared()
| nd::difference_on_axis(0)
| nd::multiply(-dt * mara::make_area(1.0));
return solution_state_t {
state.time + dt,
state.iteration + 1,
state.vertices,
state.u + du | nd::to_shared() };
}