Pourquoi le Big Data doit-il être fonctionnel?


9

J'ai commencé à travailler sur un nouveau projet récemment lié au Big Data pour mon stage. Mes gestionnaires ont recommandé de commencer à apprendre la programmation fonctionnelle (ils ont fortement recommandé Scala). J'ai eu une expérience humble en utilisant F #, mais je ne voyais pas l'importance d'utiliser ce paradigme de programmation car il est cher dans certains cas.

Dean a donné une conférence intéressante sur ce sujet et a partagé ses réflexions sur la raison pour laquelle les "Big Data" ici: http://www.youtube.com/watch?v=DFAdLCqDbLQ Mais ce n'était pas très pratique car les Big Data ne signifient pas seulement Hadoop.

Comme BigData est un concept très vague. Je l'oublie un moment. J'ai essayé de trouver un exemple simple pour comparer les différents aspects lorsque nous traitons des données, pour voir si la méthode fonctionnelle est coûteuse ou non. Si la programmation fonctionnelle coûte cher et consomme beaucoup de mémoire pour les petites données, pourquoi en avons-nous besoin pour les Big Data?

Loin des outils sophistiqués, j'ai essayé de construire une solution pour un problème spécifique et populaire en utilisant trois approches: voie impérative et voie fonctionnelle (récursivité, utilisation de collections). J'ai comparé le temps et la complexité, pour comparer entre les trois approches.

J'ai utilisé Scala pour écrire ces fonctions car c'est le meilleur outil pour écrire un algorithme en utilisant trois paradigmes

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

Manière fonctionnelle:

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))(_ + _)
}

Manière récursive:

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

Manière impérative:

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
}

J'ai remarqué que la programmation fonctionnelle est lourde! cela prend plus de temps et consomme plus d'espace en mémoire. Je suis confus, chaque fois que je lis un article ou regarde une conférence, ils disent que nous devrions utiliser la programmation fonctionnelle en science des données. C'est vrai, c'est plus facile et plus productif, spécialement dans le monde des données. mais cela prend plus de temps et plus d'espace mémoire.

Alors, pourquoi avons-nous besoin d'utiliser la programmation fonctionnelle dans le Big Data? Quelles sont les meilleures pratiques pour utiliser la programmation fonctionnelle (Scala) pour le Big Data?


5
La programmation fonctionnelle facilite la parallélisation de votre code, donc même si une seule opération peut prendre plus de temps à s'exécuter dans un thread, les performances globales peuvent être meilleures en raison du parallélisme.
Giorgio

@Giorgio: Il existe différents paradigmes comme la modélisation d'acteurs pour obtenir les meilleures performances du parallélisme. Je ne pense pas?
user3047512

2
Je suppose que c'est simplement parce que l'approche carte / réduction de hadoop est une idée de la programmation fonctionnelle.
Doc Brown

1
@ user3047512: Par exemple, Erlang utilise le modèle d'acteur et est pour la plupart fonctionnel.
Giorgio

2
La connexion entre la mode "big data" et FP n'est pas si simple. Dans "Big data", une approche dite de réduction de carte est à la mode, qui, à son tour, avait été quelque peu inspirée par l'éthique de la programmation fonctionnelle. C'est là que la similitude s'arrête, je ne vois plus de lien entre ces deux mondes.
SK-logic

Réponses:


13

Voici comment je le vois:

  • Ignorons les mots «big data» pendant un certain temps, car ils sont une notion assez vague

  • Vous avez mentionné Hadoop. Hadoop fait 2 choses: vous permet d'avoir une sorte de lecteur "virtuel" qui est distribué sur plusieurs machines, avec redondance, accessible via l'API Hadoop comme s'il s'agissait d'un seul lecteur unitaire. Cela s'appelle HDFS comme dans Hadoop Distributed File System . L'autre chose que fait Hadoop est de vous permettre d'exécuter des travaux Map-Reduce (c'est un framework pour Map-Reduce). Si nous vérifions la page Wikipedia de MapReduce , nous voyons que:

MapReduce est un modèle de programmation pour le traitement de grands ensembles de données avec un algorithme parallèle distribué sur un cluster.

...

Un programme MapReduce est composé d'une procédure Map () qui effectue le filtrage et le tri (comme le tri des étudiants par prénom dans les files d'attente, une file d'attente pour chaque nom) et une procédure Reduce () qui effectue une opération récapitulative (telle que le comptage du nombre d'élèves dans chaque file d'attente, ce qui donne des fréquences de noms)

...

'MapReduce' est un cadre pour le traitement de problèmes parallélisables à travers d'énormes ensembles de données en utilisant un grand nombre d'ordinateurs

Également sur cette page, Hadoop est décrit comme

Hadoop, l'implémentation libre et open source d'Apache de MapReduce.

Maintenant, Hadoop est écrit en Java, qui n'est pas un langage fonctionnel. En outre, si nous regardons la page de Hadoop, nous trouvons également un exemple de la façon de créer un travail MapReduce en Java et de le déployer dans un cluster Hadoop .

Voici un exemple Java d'un travail Fibonnaci MapReduce pour Hadoop.

J'espère que cela répond à votre question, à savoir que BigData, et en particulier un travail MapReduce créant Fibonacci n'a pas "besoin" d'être fonctionnel, c'est-à-dire que vous pouvez l'implémenter dans les langages OO si vous le souhaitez (par exemple).

Bien sûr, cela ne signifie pas non plus que "BigData" doit être uniquement OO. Vous pouvez très bien utiliser un langage fonctionnel pour implémenter un travail similaire à MapReduce. Vous pouvez, par exemple, utiliser Scala avec Hadoop si vous le souhaitez, via Scalding .

Je pense que d'autres points méritent d'être mentionnés.

Lorsque vous effectuez une récursivité dans Scala, si votre code le permet, Scala fera une optimisation de l'appel de queue . Cependant, étant donné que la JVM ne prend pas (encore) en charge l'optimisation des appels de queue , Scala y parvient en remplaçant, au moment de la compilation, vos appels récursifs par du code équivalent aux boucles, comme expliqué ici . Cela signifie essentiellement que faire des tests de code récursifs vs non récursifs en utilisant Scala est inutile, car ils finissent tous les deux par faire la même chose au moment de l'exécution.


2
Vous faites un excellent point sur le fait que la JVM ne prend pas en charge l'optimisation des appels de queue, ce qui sape les repères proposés par l'OP. Ceci est une réponse très informative, merci.
maple_shaft

1
Merci pour votre réponse, oui! tail-call-optimisation est l'une des fonctionnalités de scala cachées. stackoverflow.com/questions/1025181/hidden-features-of-scala/… . L'un des problèmes du "Big Data" est que chaque entreprise essaie de construire une nouvelle technologie de manière différente. Mais il y en a principalement deux: la technologie Hadoop et d'autres. Comme vous l'avez dit, c'est subjectif et lié aux problèmes eux-mêmes, nous devons également choisir le bon paradigme de programmation en fonction de notre expertise. Par exemple: les modèles prédictifs en temps réel ne fonctionnent pas très bien sur les plates-formes Hadoop.
user3047512

9

Tant que vous pouvez l'exécuter sur une seule machine, ce n'est pas du "Big Data". Votre exemple de problème est totalement inapproprié pour démontrer quoi que ce soit à ce sujet.

Le Big Data signifie que la taille des problèmes est si grande que la distribution du traitement n'est pas une optimisation mais une exigence fondamentale. Et la programmation fonctionnelle facilite l'écriture de code distribué correct et efficace en raison de structures de données immuables et de l'apatridie.


"Le Big Data signifie que la taille des problèmes est si importante que la distribution du traitement n'est pas une optimisation mais une exigence fondamentale." - Je ne comprends pas quel type de problème ne peut PAS du tout être résolu en utilisant une seule machine, et nécessite au moins N où N> 1 ...
Shivan Dragon

6
@ShivanDragon: le type de problème qui inclut des exigences de performances qui sont absolument impossibles à satisfaire sur un seul système. Ou lorsque la taille des données est si grande qu'aucun système ne peut même tout stocker.
Michael Borgwardt

Je suis désolé, je vois votre point maintenant. Est-il exact de dire que vous faites référence à, plus précisément, MapReduce qui vit sous l'égide de BigData?
Shivan Dragon

Merci pour votre contribution, je suis d'accord. Peut-être que je n'ai pas pu trouver un bon exemple simple pour démontrer mon point de vue. Le "Big Data" est toujours un moyen pour les développeurs d'utiliser les données pour résoudre nos problèmes quotidiens en tenant compte de la définition des 3V. J'oublierai le 3V pendant un moment et parlerai de l'aspect très simple, traitant des données. Si nous voyons que l'analyse des données de manière fonctionnelle coûte cher, pourquoi disons-nous que le «Big Data» doit être fonctionnel? C'est mon point.
user3047512

4
@ShivanDragon, par exemple, le LHC produit plusieurs gigaoctets de données par seconde . Pas sûr qu'une seule machine puisse même gérer un tel débit.
SK-logic

4

Je ne connais pas scala et donc je ne peux pas commenter votre approche fonctionnelle, mais votre code ressemble à exagéré.

En revanche, votre fonction récursive est inefficace. Parce que la fonction s'appelle deux fois, elle est d'ordre 2 ^ n, ce qui est très inefficace. Si vous souhaitez comparer les trois approches, vous devez comparer trois implémentations optimales.

La fonction Fibonacci peut être implémentée récursivement en appelant la fonction une seule fois. Prenons une définition plus généralisée:

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

Le cas spécial standard est:

f0 = 0
f1 = 1

La fonction récursive générale est:

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

Merci! Vous avez soulevé un bon point, mais il n'y a aucun moyen efficace de le faire de manière itérative. Il s'agit d'un problème très courant (suite Fibonacci). et c'est l'intérêt de s'attaquer au même problème de trois manières. Pouvez-vous suggérer une meilleure façon de résoudre ce problème en utilisant n'importe quel langage de programmation, je peux réécrire cela en utilisant scala et faire les mêmes tests?
user3047512

@ user3047512 Pour un langage qui prend en charge la récursivité de queue, vous pouvez l'écrire avec un accumulateur. Exemples
toasted_flakes

Scala prend également en charge la récursivité de la queue en tant que fonctionnalité cachée oldfashionedsoftware.com/2008/09/27/…
user3047512

1
@ user3047512 Parce que la solution récursive est une fonction pure (la sortie dépend uniquement des arguments de la fonction et rien d'autre ), la mémorisation est une bonne solution. En termes simples, chaque fois qu'il retournerait une valeur, stockerait les arguments et entraînerait un hachage clé / valeur, et chaque fois que la fonction serait exécutée, regardez-la d'abord. C'est l'un des avantages des fonctions pures - un futur appel à cette fonction trouvera la valeur de hachage préexistante et effectuera des calculs nuls , car nous savons que le résultat n'aura pas changé.
Izkata

@ user3047512 La version itérative ressemble également à une fonction pure dans ce cas, mais ce n'est pas toujours vrai - dans un langage fonctionnel, je pense que c'est mieux appliqué par le langage ...
Izkata

0

Si la programmation fonctionnelle coûte cher et consomme beaucoup de mémoire pour les petites données, pourquoi en avons-nous besoin pour les Big Data?

Plus précisément, je peux déjà voir quelques applications où cela est extrêmement utile. ex. Statistiques, c'est-à-dire calcul d'une fonction gaussienne à la volée avec différents paramètres ou un ensemble de paramètres pour l'analyse de données. Il existe également une interpolation pour l'analyse numérique, etc.

Quelles sont les meilleures pratiques pour utiliser la programmation fonctionnelle (Scala) pour le Big Data?

Pour répondre à l'efficacité, il existe également des techniques pour aider à augmenter votre efficacité dans l'espace ou le temps, en particulier la récursivité, la récursivité de la queue , le style de passage de continuation , les fonctions d'ordre supérieur , etc. quelque chose de simple comme la séquence de Fibonnacci, je pourrais simplement utiliser la manière impérative car je trouve parfois que certains de mes collègues sont réticents et peuvent ne pas être aussi à l'aise avec la programmation fonctionnelle et donc prendre plus de temps de développement ... (je préfère toujours utiliser la programmation fonctionnelle quand je peux [les applications dont je suis responsable]) car je le trouve rapide, propre et "facile à lire" (bien que je trouve ce code subjectif).

Wikipédia a publié une version "rapide" de la séquence des 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)
}

Utilisation de streams / hof

val fibStream:Stream[Int] = 0 #:: 1 #:: (fibStream zip fibStream.tail).map{ t => t._1 + t._2 }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.