Perché i Big Data devono essere funzionali?


9

Ho iniziato a lavorare su un nuovo progetto recentemente correlato ai Big Data per il mio tirocinio. I miei manager hanno raccomandato di iniziare ad apprendere la programmazione funzionale (hanno fortemente raccomandato Scala). Ho avuto un'esperienza umiliante con F #, ma non riuscivo a vedere l'importanza di utilizzare questo paradigma di programmazione in quanto è costoso in alcuni casi.

Dean ha tenuto un discorso interessante su questo argomento e ha condiviso le sue opinioni sul perché "Big Data" qui: http://www.youtube.com/watch?v=DFAdLCqDbLQ Ma non è stato molto conveniente poiché i Big Data non significano solo Hadoop.

Come BigData è un concetto molto vago. L'ho dimenticato per un po '. Ho cercato di trovare un semplice esempio per confrontare i diversi aspetti quando trattiamo i dati, per vedere se il modo funzionale è costoso o no. Se la programmazione funzionale è costosa e richiede molta memoria per i piccoli dati, perché ne abbiamo bisogno per i Big Data?

Lungi dall'essere strumenti fantasiosi, ho cercato di costruire una soluzione per un problema specifico e popolare usando tre approcci: il modo imperativo e il modo funzionale (ricorsione, usando le raccolte). Ho confrontato tempo e complessità, per confrontare tra i tre approcci.

Ho usato Scala per scrivere queste funzioni in quanto è lo strumento migliore per scrivere un algoritmo usando tre paradigmi

def main(args: Array[String]) {
    val start = System.currentTimeMillis()
    // Fibonacci_P
    val s = Fibonacci_P(400000000)
    val end = System.currentTimeMillis()
    println("Functional way: \n the Fibonacci sequence whose values do not exceed four million : %d \n Time : %d ".format(s, end - start))
    val start2 = System.currentTimeMillis()

    // Fibonacci_I
    val s2 = Fibonacci_I(40000000 0)
    val end2 = System.currentTimeMillis();
    println("Imperative way: \n the Fibonacci sequence whose values do not exceed four million : %d \n Time : %d ".format(s2, end2 - start2))
}

Modo funzionale:

def Fibonacci_P(max: BigInt): BigInt = {
    //http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream
    //lazy val Fibonaccis: Stream[Long] = 0 #:: 1 #:: Fibonaccis.zip(Fibonaccis.tail).map { case (a, b) => a + b }
    lazy val fibs: Stream[BigInt] = BigInt(0)#::BigInt(1)#::fibs.zip(fibs.tail).map {
        n = > n._1 + n._2
    }
    // println(fibs.takeWhile(p => p < max).toList)
    fibs.takeWhile(p = > p < max).foldLeft(BigInt(0))(_ + _)
}

Modo ricorsivo:

def Fibonacci_R(n: Int): BigInt = n match {
    case 1 | 2 = > 1
    case _ = > Fibonacci_R(n - 1) + Fibonacci_R(n - 2)
}

Modo imperativo:

def Fibonacci_I(max: BigInt): BigInt = {
    var first_element: BigInt = 0
    var second_element: BigInt = 1
    var sum: BigInt = 0

    while (second_element < max) {
        sum += second_element

        second_element = first_element + second_element
        first_element = second_element - first_element
    }

    //Return 
    sum
}

Ho notato che la programmazione funzionale è pesante! richiede più tempo e consuma più spazio in memoria. Sono confuso, ogni volta che leggo un articolo o guardo un discorso, dicono che dovremmo usare la programmazione funzionale nella scienza dei dati. È vero, è più facile e più produttivo, specialmente nel mondo dei dati. ma richiede più tempo e più spazio di memoria.

Quindi, perché dobbiamo usare la programmazione funzionale nei Big Data? Quali sono le migliori pratiche per utilizzare la programmazione funzionale (Scala) per i Big Data?


5
La programmazione funzionale semplifica il parallelismo del codice, quindi anche se una singola operazione può richiedere più tempo per essere eseguita in un thread, le prestazioni complessive possono essere migliori a causa del parallelismo.
Giorgio,

@Giorgio: Esistono diversi paradigmi come la modellazione di attori per ottenere le migliori prestazioni al parallelismo. Non la pensi così?
user3047512

2
Immagino sia semplicemente perché l'approccio mappa / riduzione di hadoop è un'idea della programmazione funzionale.
Doc Brown,

1
@ user3047512: Ad esempio, Erlang utilizza il modello dell'attore ed è per lo più funzionale.
Giorgio,

2
La connessione tra la moda dei "big data" e FP non è così semplice. In "Big data" è di moda un cosiddetto approccio di riduzione delle mappe , che a sua volta era stato in qualche modo ispirato dall'etica della programmazione funzionale. È qui che finisce la somiglianza, non riesco a vedere alcuna ulteriore connessione tra questi due mondi.
SK-logic

Risposte:


13

Ecco come lo vedo:

  • Ignoriamo le parole "big data" per un po ', in quanto sono una nozione piuttosto vaga

  • Hai menzionato Hadoop. Hadoop fa 2 cose: ti permette di avere una sorta di unità "virtuale" che è distribuita su più macchine, con ridondanza, a cui puoi accedere tramite l'API di Hadoop come se fosse una singola unità unitaria. Si chiama HDFS come nel file system distribuito di Hadoop . L'altra cosa che Hadoop fa è consentirti di eseguire lavori Map-Reduce (è un framework per Map-Reduce). Se controlliamo la pagina Wikipedia di MapReduce , vediamo che:

MapReduce è un modello di programmazione per l'elaborazione di grandi set di dati con un algoritmo distribuito parallelo su un cluster.

...

Un programma MapReduce è composto da una procedura Map () che esegue il filtraggio e l'ordinamento (come l'ordinamento degli studenti per nome nelle code, una coda per ciascun nome) e una procedura Reduce () che esegue un'operazione di riepilogo (come il conteggio del numero di studenti in ogni coda, producendo le frequenze dei nomi)

...

'MapReduce' è un framework per l'elaborazione di problemi parallelizzabili su enormi set di dati che utilizzano un gran numero di computer

Anche in questa pagina, Hadoop è descritto come

Hadoop, l'implementazione gratuita e open source di Apache di MapReduce.

Ora, Hadoop è scritto in Java, che non è un linguaggio funzionale. Inoltre, se guardiamo alla pagina di Hadoop, troviamo anche un esempio di come creare un lavoro MapReduce in Java e distribuirlo in un cluster Hadoop .

Ecco un esempio Java di un lavoro MapReduce di Fibonnaci per Hadoop.

Spero che questa risposta alla tua domanda, vale a dire che BigData, e in particolare un lavoro MapReduce che crea Fibonacci, non "necessiti" di essere funzionale, ovvero puoi implementarlo in linguaggi OO se vuoi (ad esempio).

Ovviamente ciò non significa che BigData "abbia bisogno" di essere solo OO. Puoi benissimo usare un linguaggio funzionale per implementare un lavoro simile a MapReduce. Ad esempio, puoi usare Scala con Hadoop, se lo desideri, tramite Scalding .

Altri punti che ritengo siano degni di nota.

Quando si esegue la ricorsione in Scala, se il codice lo consente, Scala eseguirà l' ottimizzazione della coda . Tuttavia, poiché la JVM non supporta (ancora) l'ottimizzazione delle chiamate in coda , Scala raggiunge questo obiettivo sostituendo, in fase di compilazione, le chiamate ricorsive con codice equivalente ai loop, come spiegato qui . Ciò significa sostanzialmente che fare benchmark di codice ricorsivo vs non ricorsivo usando Scala è inutile, poiché entrambi finiscono per fare la stessa cosa in fase di esecuzione.


2
È importante sottolineare che la JVM non supporta l'ottimizzazione delle chiamate di coda che mina i parametri di riferimento proposti dal PO. Questa è una risposta molto istruttiva, grazie.
maple_shaft

1
Grazie per la tua risposta, sì! l'ottimizzazione della coda di chiamata è una delle caratteristiche nascoste della scala. stackoverflow.com/questions/1025181/hidden-features-of-scala/… . Uno dei problemi dei "Big Data" è che ogni azienda sta cercando di costruire una nuova tecnologia in modo diverso. Ma ce ne sono principalmente due: Hadoop tech e altri. Come hai detto, è soggettivo e collegato ai problemi stessi, dovremmo scegliere il giusto paradigma di programmazione basato anche sulla nostra esperienza. Ad esempio: i modelli predittivi in ​​tempo reale non funzionano molto bene sulle piattaforme Hadoop.
user3047512

9

Finché è possibile eseguirlo su un singolo computer, non è "Big Data". Il tuo problema di esempio è del tutto inappropriato per dimostrare qualcosa al riguardo.

Big Data significa che le dimensioni del problema sono così grandi che la distribuzione dell'elaborazione non è un'ottimizzazione ma un requisito fondamentale. E la programmazione funzionale rende molto più semplice scrivere codice distribuito corretto ed efficiente a causa di strutture di dati immutabili e apolidia.


"Big Data significa che le dimensioni del problema sono così grandi che la distribuzione dell'elaborazione non è un'ottimizzazione ma un requisito fondamentale." - Non capisco che tipo di problema non possa assolutamente essere risolto utilizzando una macchina e richiede almeno N dove N> 1 ...
Shivan Dragon

6
@ShivanDragon: il tipo di problema che include requisiti di prestazione che sono assolutamente impossibili da soddisfare su un singolo sistema. O dove la dimensione dei dati è così grande che nessun singolo sistema può nemmeno memorizzarli tutti.
Michael Borgwardt,

Mi dispiace, vedo il tuo punto ora. È corretto dire che ciò a cui ti riferisci è, più specificamente, MapReduce che vive sotto l'ombrello di BigData?
Drago Shivan,

Grazie per il tuo contributo, sono d'accordo. Forse non sono riuscito a trovare un buon esempio semplice per dimostrare il mio punto di vista. "Big Data" è ancora un modo in cui gli sviluppatori utilizzano i dati per risolvere i nostri problemi quotidiani prendendo in considerazione la definizione di 3V. Per un po 'dimenticherò il 3V e parlerò dell'aspetto molto semplice, che riguarda i dati. Se vediamo che l'analisi dei dati in modo funzionale è costosa, perché diciamo che i "Big Data" devono essere funzionali? Questo è il mio punto.
user3047512

4
@ShivanDragon, ad esempio, LHC sta producendo diversi gigabyte di dati al secondo . Non sono sicuro che una singola macchina sia in grado di gestire tale produttività.
SK-logic,

4

Non conosco scala e quindi non posso commentare il tuo approccio funzionale, ma il tuo codice sembra eccessivo.

La tua funzione ricorsiva invece è inefficiente. Poiché la funzione si chiama due volte, è dell'ordine di 2 ^ n, che è altamente inefficiente. Se si desidera confrontare i tre approcci, è necessario confrontare tre implementazioni ottimali.

La funzione Fibonacci può essere implementata in modo ricorsivo chiamando la funzione una sola volta. Prendiamo una definizione più generalizzata:

F(0) = f0
F(1) = f1
F(n) = F(n-1) + F(n-2)

Il caso speciale standard è:

f0 = 0
f1 = 1

La funzione ricorsiva generale è:

function fibonacci($f0, $f1, $n){
    if($n < 0 || !isInt($n)) return false;
    if($n = 0) return $f0;
    if($n = 1) return $f1;
    return fibonacci($f1, $f0 + $f1, $n - 1);
}

Grazie! Hai sollevato un buon punto, ma non esiste un modo efficace per farlo in modo iterativo. Questa è una prob molto comune (suite di Fibonacci). e questo è il punto di affrontare lo stesso problema usando tre modi. Puoi suggerire un modo migliore per risolvere questo problema usando qualsiasi linguaggio di programmazione, posso riscriverlo usando Scala e fare gli stessi test?
user3047512

@ user3047512 Per una lingua che supporta la ricorsione della coda, è possibile scriverla con un accumulatore. Esempi
toasted_flakes il

Scala supporta anche la ricorsione della coda come funzione nascosta oldfashionedsoftware.com/2008/09/27/…
user3047512

1
@ user3047512 Poiché la soluzione ricorsiva è una funzione pura (l'output dipende esclusivamente dalla funzione args e nient'altro ), la memoizzazione è una buona soluzione. In parole povere, ogni volta che restituisce un valore, memorizza gli arg e si traduce in un hash chiave / valore e ogni volta che viene eseguita la funzione, guarda prima lì. Questo è uno dei vantaggi delle funzioni pure: una chiamata futura a questa funzione troverà il valore hash preesistente e farà zero calcoli, perché sappiamo che il risultato non sarà cambiato.
Izkata,

@ user3047512 Anche la versione iterativa sembra una pura funzione in questo caso, ma non è sempre vero - in un linguaggio funzionale, credo che sia meglio applicato dal linguaggio ...
Izkata

0

Se la programmazione funzionale è costosa e richiede molta memoria per i piccoli dati, perché ne abbiamo bisogno per i Big Data?

In particolare posso già vedere alcune applicazioni in cui questo è estremamente utile. ex. Statistiche, ovvero il calcolo al volo di una funzione gaussiana con parametri diversi o un insieme di parametri per l'analisi dei dati. Esiste anche un'interpolazione per l'analisi numerica, ecc.

Quali sono le migliori pratiche per utilizzare la programmazione funzionale (Scala) per i Big Data?

Per rispondere sull'efficienza ci sono anche tecniche per aumentare l'efficienza nello spazio o nel tempo, in particolare ricorsione, ricorsione della coda , stile di passaggio di continuazione , funzioni di ordine superiore , ecc. Alcune lingue hanno i loro pro e contro (ad esempio pigro vs desideroso). qualcosa di semplice come la sequenza di Fibonnacci, potrei semplicemente usare l'imperativo come trovo a volte alcuni dei miei collaboratori sono riluttanti e potrebbero non essere a proprio agio con la programmazione funzionale e quindi impiegare più tempo di sviluppo ... (Preferisco ancora usare la programmazione funzionale quando posso [applicazioni di cui sono responsabile]) poiché lo trovo veloce, pulito e "facile da leggere" (anche se trovo questo soggettivo) il codice.

Wikipedia ha pubblicato una versione "veloce" della sequenza fibonnacci. https://en.wikipedia.org/wiki/Functional_programming#Scala

def fibTailRec(n: Int): Int = {
  @tailrec def f(a: Int, b: Int, c: Int): Int = if (a == 0) 0 else if(a < 2) c else f(a-1, c, b + c)
  f(n, 0, 1)
}

Utilizzo di stream / hof

val fibStream:Stream[Int] = 0 #:: 1 #:: (fibStream zip fibStream.tail).map{ t => t._1 + t._2 }
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.