Perché i Big Data devono essere funzionali?

8

Ho iniziato a lavorare su un nuovo progetto recentemente correlato a Big Data per il mio stage. I miei manager consigliarono di iniziare ad apprendere la programmazione funzionale (altamente raccomandato Scala). Ho avuto un'esperienza umiliata usando F #, ma non ho potuto vedere l'importante di usare questo paradigma di programmazione in quanto è costoso in alcuni casi.

Dean ha tenuto un discorso interessante su questo argomento e ha condiviso i suoi pensieri sul perché "Big Data" qui: link Ma non era molto conveniente dato che Big Data non significa 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 consuma la memoria per i piccoli dati, perché ne abbiamo bisogno per i Big Data?

Lontano da strumenti di fantasia, ho cercato di costruire una soluzione per un problema specifico e diffuso utilizzando tre approcci: modo imperativo e modo funzionale (ricorsione, utilizzo di collezioni). Ho confrontato il tempo e la complessità, per confrontare 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 nella 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 ci vuole più tempo e più spazio di memoria.

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

    
posta user3047512 07.12.2013 - 12:40
fonte

4 risposte

13

Ecco come la vedo io:

  • Ignoriamo le parole "big data" per un po ', dato che sono una nozione abbastanza vaga

  • Hai menzionato Hadoop. Hadoop fa 2 cose: ti permette di avere una sorta di drive "virtuale" che è distribuito su più macchine, con ridondanza, a cui si può accedere tramite l'API di Hadoop come se fosse un'unità singola, unitaria. Si chiama HDFS come in Hadoop Distributed File System . L'altra cosa che fa Hadoop è consentire l'esecuzione di lavori di Riduzione mappe (è un framework per Ridurre mappa). Se verifichiamo la pagina Wikipedia di MapReduce , vediamo che:

MapReduce is a programming model for processing large data sets with a parallel, distributed algorithm on a cluster.

...

A MapReduce program is composed of a Map() procedure that performs filtering and sorting (such as sorting students by first name into queues, one queue for each name) and a Reduce() procedure that performs a summary operation (such as counting the number of students in each queue, yielding name frequencies)

...

'MapReduce' is a framework for processing parallelizable problems across huge datasets using a large number of computers

Anche in questa pagina, Hadoop è descritto come

Hadoop, Apache's free and open source implementation of 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 Fibronaci MapReduce per Hadoop.

Spero che questo risponda alla tua domanda, ovvero che i BigData, e in particolare un lavoro MapReduce che crea Fibonacci, non ha "bisogno" di essere funzionale, alias tu puoi implementarlo nelle lingue OO se vuoi (per esempio).

Ovviamente questo non significa che i "bisogni" di BigData siano OO-solo. Puoi benissimo usare un linguaggio funzionale per implementare un lavoro simile a MapReduce. Ad esempio, puoi utilizzare Scala con Hadoop se lo desideri, tramite Scalding .

Altri punti che ritengo degni di menzionare.

Quando esegui la ricorsione in Scala, se il tuo codice lo consente, Scala eseguirà ottimizzazione della chiamata di coda . Tuttavia, poiché la JVM non (ancora) supporta l'ottimizzazione della coda di chiamata , Scala raggiunge questo sostituendo, al momento della compilazione, le tue chiamate ricorsive con codice equivalente a cicli, come spiegato qui . Ciò significa in sostanza che eseguire benchmark ricorsivi e non ricorsivi del codice utilizzando Scala è inutile, poiché entrambi finiscono per fare la stessa cosa in fase di esecuzione.

    
risposta data 07.12.2013 - 16:00
fonte
8

Finché è possibile eseguirlo su una singola macchina, non si tratta di "Big Data". Il tuo problema di esempio è completamente inappropriato per dimostrare qualcosa a riguardo.

Big Data significa che le dimensioni dei problemi sono così grandi che la distribuzione dell'elaborazione non è un'ottimizzazione, ma un requisito fondamentale. E la programmazione funzionale semplifica notevolmente la scrittura di codice distribuito corretto ed efficiente grazie a strutture dati immutabili e all'apolidia.

    
risposta data 07.12.2013 - 14:53
fonte
4

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

La tua funzione ricorsiva d'altra parte è inefficiente. Poiché la funzione chiama se stessa due volte, è di ordine 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ù generale:

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);
}
    
risposta data 08.12.2013 - 14:09
fonte
0

If functional programming is expensive and memory-consuming for small data, why do we need it for Big Data ?

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

What are the best practices to use functional programming (Scala) for Big Data ?

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

Wikipedia ha una versione "veloce" della sequenza di Fibonnacci pubblicata. link

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)
}

Uso di stream / hof

val fibStream:Stream[Int] = 0 #:: 1 #:: (fibStream zip fibStream.tail).map{ t => t._1 + t._2 }
    
risposta data 01.12.2017 - 18:51
fonte

Leggi altre domande sui tag