Programmazione funzionale e calcolo scientifico


42

Mi scuso se questa è una domanda vaga, ma qui va:

Negli ultimi anni, la programmazione funzionale ha ricevuto molta attenzione nella comunità dell'ingegneria del software. Molti hanno iniziato a usare linguaggi come Scala e Haskell e hanno affermato il successo rispetto ad altri linguaggi di programmazione e paradigmi. La mia domanda è: come esperti di calcolo scientifico / scientifico ad alte prestazioni, dovremmo essere interessati alla programmazione funzionale? Dovremmo partecipare a questa mini rivoluzione?

Quali sono i pro e i contro della programmazione funzionale nel settore di lavoro SciComp?


2
Perché mettersi di proposito in una giacca dritta? Gli effetti collaterali sono uno strumento; è essenziale per le applicazioni del mondo reale. Se desideri cpu ed efficienza della memoria, i linguaggi di programmazione funzionale non sarebbero sul mio radar. Programmi che richiedono un controllo automatico di verifica / correttezza (ad esempio, per l'utilizzo in una struttura nucleare?), Quindi ok potrebbe esserci un caso.
Coda degli apprendisti

Risposte:


34

Ho fatto solo un po 'di programmazione funzionale, quindi prendi questa risposta con un pizzico di sale.

Professionisti:

  • La programmazione funzionale sembra molto matematica; è un bel paradigma per esprimere alcuni concetti matematici
  • Ci sono buone librerie disponibili per cose come la verifica formale dei programmi e la dimostrazione del teorema, quindi è possibile scrivere programmi che ragionino sui programmi - questo aspetto è buono per la riproducibilità
  • Puoi eseguire la programmazione funzionale in Python e C ++ tramite espressioni lambda; puoi anche fare la programmazione funzionale in Julia e Mathematica
  • Non molte persone lo usano, quindi puoi essere un pioniere. Proprio come c'erano i primi utenti di MATLAB, Python, R e ora Julia, è necessario che i primi utenti della programmazione funzionale possano attecchire

Contro:

  • I linguaggi che in genere sono pensati come linguaggi di programmazione funzionale, come Haskell, OCaml (e altri dialetti ML), e Lisp sono generalmente considerati lenti rispetto ai linguaggi usati per il calcolo scientifico critico per le prestazioni. OCaml è, nella migliore delle ipotesi, circa la metà più veloce di C.
  • Queste lingue mancano di infrastrutture di biblioteca rispetto alle lingue comunemente usate nella scienza computazionale (Fortran, C, C ++, Python); se vuoi risolvere un PDE, è molto più facile farlo in una lingua più comunemente usata nella scienza computazionale di una che non lo è.
  • Non c'è così tanto di una comunità scientifica computazionale che usa linguaggi di programmazione funzionale come usa linguaggi procedurali, il che significa che non avrai molto aiuto per impararlo o eseguirne il debug e le persone probabilmente ti daranno una cazzata per usandolo (che tu lo meriti o no)
  • Lo stile della programmazione funzionale è diverso da quello utilizzato nella programmazione procedurale, che viene generalmente insegnato nelle classi introduttive di informatica e nelle classi di tipo "MATLAB per scienziati e ingegneri"

Penso che molte delle obiezioni nella sezione "Contro" potrebbero essere superate. Poiché è un punto di discussione comune su questo sito di scambio di stack, il tempo degli sviluppatori è più importante del tempo di esecuzione. Anche se i linguaggi di programmazione funzionale sono lenti, se parti critiche per le prestazioni possono essere delegate a un linguaggio procedurale più veloce e se i guadagni di produttività possono essere dimostrati attraverso un rapido sviluppo delle applicazioni, potrebbe valerne la pena utilizzarli. Vale la pena notare qui che i programmi implementati in puro Python, puro MATLAB e puro R sono notevolmente più lenti delle implementazioni di questi stessi programmi in C, C ++ o Fortran. Linguaggi come Python, MATLAB e R sono popolari proprio perché scambiano la velocità di esecuzione con la produttività e anche allora, Python e MATLAB dispongono entrambi di funzionalità per l'implementazione di interfacce per il codice compilato in C o C ++ in modo che il codice critico per le prestazioni possa essere implementato per essere eseguito rapidamente. La maggior parte delle lingue ha un'interfaccia di funzione estranea a C, che sarebbe sufficiente per interfacciarsi con la maggior parte delle biblioteche di interesse per gli scienziati computazionali.

Dovresti essere interessato alla programmazione funzionale?

Tutto dipende da cosa pensi che sia bello. Se sei il tipo di persona che è disposta a invertire la convenzione e sei disposta a passare attraverso lo slogan di evangelizzare alla gente sulle virtù di qualunque cosa tu voglia fare con la programmazione funzionale, direi che provaci . Mi piacerebbe vedere le persone fare cose interessanti con la programmazione funzionale nella scienza computazionale, se non altro per dimostrare che tutti gli oppositori si sbagliano (e ci saranno molti oppositori). Se non sei il tipo di persona che vuole avere a che fare con un gruppo di persone che ti chiedono "Perché diavolo usi un linguaggio di programmazione funzionale invece di (inserisci qui il loro linguaggio di programmazione procedurale preferito)?", Allora non vorrei non preoccuparti.

C'è stato un certo uso di linguaggi di programmazione funzionale per lavori ad alta intensità di simulazione. La società di trading quantitativa Jane Street utilizza OCaml per la modellizzazione finanziaria e l'esecuzione delle sue strategie di trading. OCaml è stato utilizzato anche in FFTW per generare del codice C utilizzato nella libreria. Liszt è un linguaggio specifico del dominio sviluppato a Stanford e implementato in Scala che viene utilizzato per risolvere PDE. La programmazione funzionale è sicuramente utilizzata nell'industria (non necessariamente nella scienza computazionale); resta da vedere se decollerà nella scienza computazionale.


4
Vorrei contribuire per aggiungere un pro e un contro. Pro :: flessibilità del codice: poiché tutto è una funzione, puoi sempre chiamare quella funzione con un'altra funzione; questo è estremamente potente. Con :: code readability: i codici di programmazione funzionale sono davvero difficili da leggere; anche per (la maggior parte) delle persone che le hanno scritte. Adesso mi ci vuole anche un po 'di tempo per capire alcuni vecchi codici che ho scritto per risolvere alcuni problemi PDE generici con B-splines in Mathematica 6 mesi fa; Estraggo sempre quel codice quando voglio spaventare alcuni colleghi ;-).
SEB

4
L'unica aggiunta che aggiungerei è: Con :: consumo di memoria . Molte copie devono essere fatte per eliminare gli effetti collaterali.
Matthew Emmett,

1
@StefanSmith: (i) So che a volte viene utilizzato nella ricerca (ad esempio, Maxima è un CAS basato su Lisp); oltre a ciò, non conosco la parte superiore della mia testa. (ii) Nessuna idea. Gran parte della mia risposta si basava su prove aneddotiche raccolte dalle conversazioni che ho avuto negli ultimi anni.
Geoff Oxberry,

@seb, sembra che tu stia descrivendo proprietà di linguaggi funzionali simili a Lisp che non si applicano altrettanto bene ai linguaggi funzionali simili a Haskell.
Mark S.

1
Grande voto per il commento di @MatthewEmmett. La copia può essere molto costosa per calcoli ad alte prestazioni.
Charles,

10

Forse ho una prospettiva unica su questo perché sono un professionista dell'HPC con un background di calcolo scientifico e un utente di linguaggio di programmazione funzionale. Non voglio equiparare HPC al calcolo scientifico, ma esiste una notevole intersezione, e questo è il punto di vista che prendo per rispondere a questo.

È improbabile che i linguaggi funzionali vengano adottati in HPC per ora principalmente perché gli utenti e i clienti HPC si preoccupano sinceramente di raggiungere il più vicino possibile alle massime prestazioni. È vero che quando il codice è scritto in modo funzionale espone naturalmente il parallelismo che può essere sfruttato, ma in HPC non è sufficiente. Il parallelismo è solo un pezzo del puzzle nel raggiungere alte prestazioni, è necessario tenere conto anche di una vasta gamma di dettagli micro-architettonici e, in generale, questo richiede un controllo molto preciso sull'esecuzione del codice, che il controllo non è disponibile in alcun linguaggi funzionali che conosco.

Detto questo, ho grandi speranze che questo possa cambiare. Ho notato una tendenza secondo cui i ricercatori stanno iniziando a rendersi conto che molte di queste ottimizzazioni microarchitettura possono essere automatizzate (fino a un certo punto). Ciò ha generato uno zoo di tecnologia di compilazione da sorgente a sorgente in cui un utente inserisce una "specifica" del calcolo che desidera che avvenga, e il compilatore genera il codice C o Fortran che realizza quel calcolo con le ottimizzazioni e il parallelismo necessari per l'efficienza usa l'architettura di destinazione. Per inciso, questo è ciò che i linguaggi funzionali sono ben adattati a fare: modellare e analizzare i linguaggi di programmazione. Non è un caso che i primi ad adottare i linguaggi funzionali siano stati sviluppatori di compilatori. Con alcune notevoli eccezioni, non ho ancora visto che questo ha preso piede, ma le idee ci sono,


8

Vorrei aggiungere un aspetto alle altre due risposte. A parte l'ecosistema, la programmazione funzionale offre una grande opportunità per l'esecuzione parallela come il multithreading o il calcolo distribuito. Le sue intrinseche proprietà di immutabilità lo rendono adatto al parallelismo, che è generalmente un vero dolore nel * bip * quando si tratta di linguaggi imperativi.

Poiché il miglioramento delle prestazioni hardware negli ultimi anni si è concentrato sull'aggiunta di core ai processori invece di spingere le frequenze più alte, il calcolo parallelo sta diventando molto più popolare (scommetto che lo sapete tutti).

Un'altra cosa che Geoff menziona è che il tempo degli sviluppatori è spesso più importante del tempo di esecuzione. Lavoro per un'azienda che costruisce un SaaS ad alta intensità computazionale e abbiamo fatto un test iniziale delle prestazioni all'inizio, inserendo C ++ vs Java. Abbiamo scoperto che C ++ ha fornito un taglio di circa il 50% nei tempi di esecuzione su Java (questo era per la geometria computazionale e le cifre probabilmente varieranno a seconda dell'applicazione), ma siamo comunque andati con Java a causa dell'importanza del tempo degli sviluppatori e speravamo che le ottimizzazioni e i futuri miglioramenti delle prestazioni hardware ci aiuterebbero ad arrivare sul mercato. Posso dire con fiducia che se avessimo scelto diversamente, non saremmo ancora in affari.

Ok, ma Java non è un linguaggio di programmazione funzionale, quindi cosa potresti fare con qualsiasi cosa, potresti chiedere. Bene, in seguito, quando abbiamo impiegato più sostenitori del paradigma funzionale e inciampato sulla necessità della paralellizzazione, abbiamo migrato progressivamente parti del nostro sistema su Scala, che combina gli aspetti positivi della programmazione funzionale con il potere dell'imperativo e si fonde bene con Giava. Ci ha aiutato moltissimo ad aumentare le prestazioni del nostro sistema con il minimo mal di testa e probabilmente continuerà a trarre beneficio da ulteriori aumenti delle prestazioni nel settore hardware quando più core saranno stipati nei processori di domani.

Nota che sono totalmente d'accordo con i contro citati nelle altre risposte, ma ho pensato che la facilitazione dell'esecuzione parallela sia un professionista così potente che non poteva non essere menzionato.


8

Geoff ha già dato una buona panoramica dei motivi a cui ho poco da aggiungere oltre a sottolineare uno dei suoi punti: l'ecosistema. Che tu stia sostenendo la programmazione funzionale o qualsiasi altro paradigma, una delle domande importanti che devi affrontare è che c'è un'incredibile quantità di software su cui tutti gli altri possono basarsi e che devi riscrivere. Esempi sono MPI, PETSc o Trilinos per l'algebra lineare o una qualsiasi delle librerie di elementi finiti, tutte scritte in C o C ++. C'è un'enorme quantità di inerzia nel sistema, forse non perché tutti pensano che C / C ++ sia in realtà il miglior linguaggio in cui scrivere software di calcolo, ma perché molte persone hanno trascorso anni della loro vita a creare qualcosa che è utile per molte persone.

Penso che la maggior parte delle persone computazionali concorderanno sul fatto che c'è molto valore nel provare nuovi linguaggi di programmazione e nel valutare la loro idoneità a questo problema. Ma sarà un momento difficile e solitario perché non sarai in grado di produrre risultati competitivi con quello che fanno gli altri. Potrebbe anche darti la reputazione di qualcuno che ha iniziato la prossima mossa verso un diverso paradigma di programmazione. Ehi, ci sono voluti solo C ++ circa 15 anni per sostituire Fortran!


6
E C ++ è, nella migliore delle ipotesi, solo a metà strada per sostituire effettivamente Fortran in questo spazio. Vediamo sempre nuovi codici in Fortran e un sacco di legacy uno da avviare!
Bill Barth,

2
Il C ++ (a differenza di Fortran) è troppo complicato sia per imparare che per usare. Nuovi codici scientifici open source sono ancora in fase di scrittura in Fortran. Notevoli nella mia zona (Scienze della Terra) sono PFlotran, SPECFEM3D, GeoFEM ecc. Idem per quasi tutti i nuovi codici nelle scienze dell'atmosfera. C ++ IMHO non ha nemmeno sostituito ciò che avrebbe dovuto sostituire (C).
Stali,

1
Dovresti provare Wolfgang a Fortran, è un'ottima lingua, facile da imparare / scrivere e la velocità non ti deluderà.
Ondřej Čertík,

3
Non mi interessa la velocità (bene, faccio un po ', ma non è la considerazione generale che è per gli altri). Ciò che conta per me è quanto tempo mi serve per programmare un algoritmo complesso, e Fortran perde da questo punto di vista perché il linguaggio è così semplice. Nessuna libreria standard di cui parlare, nessun modello per consentire il codice generico, l'orientamento degli oggetti mezzo assed. Fortran semplicemente non è la mia lingua e, francamente, non dovrebbe esserlo nemmeno per quasi tutte le altre persone di informatica scientifica.
Wolfgang Bangerth,

4
@StefanSmith: Sì. Potrebbe essere un'idea difendibile nell'informatica scientifica (dove direi ancora che è obsoleta e improduttiva). Non è certamente difendibile per quanto riguarda l'educazione degli studenti - perché la maggior parte dei nostri studenti lascia il mondo accademico e nell'industria praticamente nessuno usa Fortran.
Wolfgang Bangerth,

7

Il breve riassunto è quello

  1. Il calcolo numerico utilizza mutabilità / effetti collaterali per raggiungere la maggior parte dei suoi accelerazioni e ridurre le allocazioni (molte strutture di programmazione funzionale hanno dati immutabili)
  2. La valutazione lenta può essere approssimativa da utilizzare con codici numerici.
  3. O stai sviluppando un pacchetto in cui scendere ai livelli più bassi per le prestazioni è davvero importante (C / Fortran o ora Julia) (in questi puoi anche modificare il codice assembler come necessario), oppure stai scrivendo uno script che utilizza queste librerie veloci quindi tendi principalmente a preoccuparti dei tempi di sviluppo (e quindi scegli Julia / MATLAB / Python / R). I linguaggi funzionali tendono a trovarsi in una strana via di mezzo che è utile in altre discipline, ma non altrettanto utile qui.
  4. XnXn+1

Questi fatti insieme rendono la programmazione funzionale non sembra necessaria alla maggior parte degli utenti.


+1, ma un'aggiunta al punto 3: penso che le caratteristiche funzionali nei linguaggi di alto livello siano abbastanza utili e molti dei vantaggi dei linguaggi funzionali menzionati in altre risposte (ad es. Una facile parallelizzazione) tendono ad applicarsi a questo scenario.
Szabolcs,

3

Penso che sia interessante notare che l'uso della programmazione funzionale nella scienza computazionale non è una novità. Ad esempio, questo articolo del 1990 ha mostrato come migliorare le prestazioni dei programmi numerici scritti in Lisp (forse il primo linguaggio di programmazione funzionale) usando una valutazione parziale. Questo lavoro faceva parte di una catena di strumenti usata in un articolo del 1992 di GJ Sussman (di fama SICP ) e J Wisdom che forniva prove numeriche del comportamento caotico del Sistema Solare . Maggiori dettagli sull'hardware e sul software coinvolti in tale calcolo sono disponibili qui .


1

R è una lingua funzionale e anche una statistica (e ora Machine learning) e in realtà la lingua numero 1 per le statistiche. Tuttavia, non è un linguaggio HPC: non viene utilizzato per il "crunching numerico" tradizionale come simulazioni di fisica, ecc. Ma può essere eseguito su cluster di grandi dimensioni (ad esempio tramite MPI) per simulazioni di statistiche di massa (MCMC) dell'apprendimento automatico.

Mathematica è anche un linguaggio funzionale ma il suo dominio principale è il calcolo simbolico piuttosto che il calcolo numerico.

In Julia puoi anche programmare in uno stile funzionale (accanto a procedurale e al loro sapore di OO (multi-dispatch)) ma non è puro (le strutture di dati di base sono tutte mutabili (tranne che per le tuple), anche se ci sono alcune librerie con immutabile strutture di dati funzionali. Ancora più importante, è molto più lento dello stile procedurale, quindi non è molto usato.

Non definirei Scala un linguaggio funzionale ma piuttosto un ibrido funzionale all'oggetto. Puoi usare molti concetti funzionali in Scala. Scala è importante per il cloud computing a causa di Spark ( https://spark.apache.org/ ).

Nota che il moderno Fortran ha in realtà alcuni elementi di programmazione funzionale: ha una semantica puntatore rigoroso (a differenza di C), puoi avere funzioni pure (senza effetti collaterali) (e contrassegnarlo come tale) e puoi avere l'immutabilità. Ha anche l'indicizzazione intelligente in cui è possibile specificare le condizioni per gli indici di matrice. Questo è simile a una query e normalmente si trova solo in un linguaggio di alto livello come R di LINQ in C # o tramite funzioni di filtro di ordine superiore in linguaggi funzionali. Quindi Fortran non è poi così male, ha anche alcune caratteristiche piuttosto moderne (ad es. Co-array) che non si trovano in molte lingue. In effetti nelle versioni future di Fortran preferirei vedere più funzioni funzionali aggiunte invece delle funzionalità OO (che ora è di solito il caso) perché OO in Fortran è davvero scomodo e brutto.


1

I professionisti sono gli "strumenti" integrati in ogni linguaggio funzionale: è così facile filtrare i dati, è così facile iterare i dati ed è molto più facile trovare una soluzione chiara e concisa ai tuoi problemi.

L'unico svantaggio è che devi concentrarti su questo nuovo tipo di pensiero: potrebbe volerci del tempo per imparare ciò che devi sapere. Altri nel dominio SciComp in realtà non usano queste lingue, il che significa che non puoi ottenere così tanto supporto :(

Se sei interessato a linguaggi funzionali-scientifici, ne ho sviluppato uno https://ac1235.github.io


1

Ecco i miei argomenti sul perché la programmazione funzionale può e dovrebbe essere utilizzata per la scienza computazionale. I vantaggi sono enormi e gli svantaggi stanno rapidamente scomparendo. Nella mia mente c'è solo un aspetto negativo:

Contro : mancanza di supporto linguistico in C / C ++ / Fortran

Almeno in C ++, questo truffatore sta scomparendo, poiché C ++ 14/17 ha aggiunto potenti funzionalità per supportare la programmazione funzionale. Potrebbe essere necessario scrivere tu stesso un codice di libreria / supporto, ma la lingua sarà tua amica. Ad esempio, ecco una libreria (warning: plug) che esegue immutabili array multidimensionali in C ++: https://github.com/jzrake/ndarray-v2 .

Inoltre, ecco un link a un buon libro sulla programmazione funzionale in C ++, sebbene non sia focalizzato su applicazioni scientifiche.

Ecco il mio riassunto di ciò che credo siano i professionisti:

Pro :

  • Correttezza
  • Comprensibilità
  • Prestazione

In termini di correttezza , i programmi funzionali sono manifestamente ben posizionati : ti costringono a definire correttamente lo stato minimo delle variabili fisiche e la funzione che avanza nello stato in avanti nel tempo:

int main()
{
    auto state = initial_condition();

    while (should_continue(state))
    {
        state = advance(state);
        side_effects(state);
    }
    return 0;
}

Risolvere un'equazione differenziale parziale (o ODE) è perfetto per la programmazione funzionale; stai solo applicando una funzione pura (advance ) alla soluzione corrente per generare quella successiva.

Nella mia esperienza, il software di simulazione fisica è generalmente gravato da una cattiva gestione dello stato . Di solito, ogni fase dell'algoritmo opera su un pezzo di uno stato condiviso (effettivamente globale). Ciò rende difficile, o addirittura impossibile, garantire il corretto ordine delle operazioni, lasciando il software vulnerabile a bug che possono manifestarsi come errori seg, o peggio, termini di errore che non bloccano il codice ma compromettono silenziosamente l'integrità della sua scienza produzione. Il tentativo di gestire lo stato condiviso in una simulazione fisica inibisce anche il multi-threading, il che è un problema per il futuro, poiché i supercomputer si stanno muovendo verso conteggi core più alti e il ridimensionamento con MPI spesso supera i compiti di ~ 100k. Al contrario, la programmazione funzionale rende banale il parallelismo della memoria condivisa, a causa dell'immutabilità.

Prestazione sono migliorate anche nella programmazione funzionale grazie alla valutazione pigra degli algoritmi (in C ++, ciò significa generare molti tipi in fase di compilazione, spesso uno per ogni applicazione di una funzione). Ma riduce il sovraccarico di accessi e allocazioni di memoria, oltre a eliminare l'invio virtuale - consentendo al compilatore di ottimizzare un intero algoritmo vedendo subito tutti gli oggetti funzione che lo compongono. In pratica, sperimenterai diverse disposizioni dei punti di valutazione (in cui il risultato dell'algoritmo viene memorizzato nella cache in un buffer di memoria) per ottimizzare l'uso della CPU rispetto alle allocazioni di memoria. Ciò è piuttosto semplice a causa dell'alta località (vedi l'esempio seguente) delle fasi dell'algoritmo rispetto a ciò che vedrai in genere in un modulo o in un codice basato su classi.

I programmi funzionali sono più facili da capire in quanto banalizzano lo stato della fisica. Ciò non significa che la loro sintassi sia facilmente comprensibile da tutti i tuoi colleghi! Gli autori dovrebbero fare attenzione a usare funzioni ben denominate e i ricercatori in generale dovrebbero abituarsi a vedere gli algoritmi espressi in modo funzionale anziché procedurale. Devo ammettere che l'assenza di strutture di controllo può essere scoraggiante per alcuni, ma non credo che ciò dovrebbe impedirci di andare nel futuro in grado di fare scienza di qualità migliore sui computer.

Di seguito è riportata una advancefunzione di esempio , adattata da un codice a volume finito usando il ndarray-v2pacchetto. Nota gli to_sharedoperatori: questi sono i punti di valutazione a cui alludevo prima.

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() };
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.