web-dev-qa-db-fra.com

Apache Spark: Le nombre de cœurs par rapport au nombre d'exécuteurs

J'essaie de comprendre la relation entre le nombre de cœurs et le nombre d'exécuteurs lors de l'exécution d'un travail Spark sur YARN.

L'environnement de test est le suivant:

  • Nombre de nœuds de données: 3
  • Spécification de la machine du noeud de données:
    • CPU: Core i7-4790 (nombre de cœurs: 4, nombre de threads: 8)
    • RAM: 32 Go (8 Go x 4)
    • Disque dur: 8 To (2 To x 4)
  • Réseau: 1Go

  • Version Spark: 1.0.0

  • Version Hadoop: 2.4.0 (Hortonworks HDP 2.1)

  • Flux de travaux Spark: sc.textFile -> filtre -> carte -> filtre -> carteToPair -> réductionByKey -> carte -> saveAsTextFile

  • Des données d'entrée

    • Type: fichier texte unique
    • Taille: 165 Go
    • Nombre de lignes: 454 568 833
  • Sortie

    • Nombre de lignes après le deuxième filtre: 310 640 717
    • Nombre de lignes du fichier de résultat: 99 848 268
    • Taille du fichier de résultat: 41 Go

Le travail a été exécuté avec les configurations suivantes:

  1. --master yarn-client --executor-memory 19G --executor-cores 7 --num-executors 3 (exécuteurs par nœud de données, utiliser autant que les cœurs)

  2. --master yarn-client --executor-memory 19G --executor-cores 4 --num-executors 3 (nombre de cœurs réduit)

  3. --master yarn-client --executor-memory 4G --executor-cores 2 --num-executors 12 (moins de noyau, plus exécuteur)

Temps écoulés:

  1. 50 min 15 sec

  2. 55 min 48 sec

  3. 31 min 23 sec

À ma grande surprise, (3) était beaucoup plus rapide.
Je pensais que (1) serait plus rapide, car il y aurait moins de communication entre exécuteurs lors du brassage.
Bien que le nombre de cœurs de (1) soit inférieur à (3), le nombre de cœurs n’est pas le facteur clé puisque 2) a bien fonctionné.

(Des suivis ont été ajoutés après la réponse de pwilmot.)

Pour information, la capture d'écran du moniteur de performances est la suivante:

  • Résumé du noeud de données Ganglia pour (1) - travail démarré à 04h37.

Ganglia data node summary for (1)

  • Résumé du noeud de données Ganglia pour (3) - le travail a commencé à 19h47. S'il vous plaît ignorer le graphique avant ce moment.

Ganglia data node summary for (3)

Le graphique se divise grossièrement en 2 sections:

  • D'abord: du début à réduireByKey: utilisation intensive du processeur, aucune activité réseau
  • Deuxièmement: après la réduction de la clé: la CPU s'abaisse, l'E/S réseau est effectuée.

Comme le graphique l'indique, (1) peut utiliser autant de puissance de processeur qu'il lui a été donné. Donc, ce n'est peut-être pas le problème du nombre de threads.

Comment expliquer ce résultat?

174
zeodtr

Pour rendre tout cela un peu plus concret, voici un exemple concret de configuration d'une application Spark pour qu'elle utilise le maximum de cluster possible: Imaginez un cluster avec six nœuds exécutant NodeManagers, chacun équipé de 16 cœurs et de 64 Go de mémoire . Les capacités de NodeManager, yarn.nodemanager.resource.memory-mb et yarn.nodemanager.resource.cpu-vcores, devraient probablement être définies sur 63 * 1024 = 64512 (mégaoctets) et 15 respectivement. Nous évitons d'allouer 100% des ressources aux conteneurs YARN car le nœud a besoin de certaines ressources pour exécuter les démons OS et Hadoop. Dans ce cas, nous laissons un gigaoctet et un noyau pour ces processus système. Cloudera Manager vous aide en les comptabilisant et en configurant automatiquement ces propriétés YARN.

La première impulsion probable serait d’utiliser - num-executors 6 --executor-cores 15 --executor-memory 63G . Cependant, cette approche est erronée pour les raisons suivantes:

63 Go + la surcharge de mémoire de l’exécuteur ne correspond pas à la capacité de 63 Go des NodeManagers. Le maître d’application occupera un noyau sur l’un des nœuds, ce qui signifie qu’il n’y aura pas de place pour un exécuteur à 15 cœurs sur ce nœud. 15 cœurs par exécuteur peuvent entraîner un débit insuffisant des E/S HDFS.

Une meilleure option serait d’utiliser - num-exécuteurs 17 --executor-cores 5 --executor-memory 19G . Pourquoi?

Cette configuration a pour résultat trois exécuteurs sur tous les nœuds, à l'exception de celui avec AM, qui aura deux exécuteurs. --executor-memory a été calculé comme suit (63/3 exécuteurs par nœud) = 21. 21 * 0.07 = 1,47. 21 - 1,47 ~ 19.

L'explication a été donnée dans un article du blog de Cloudera, How-to: Optimisez votre Apache Spark Jobs (Part 2) .

54
DzOrdre

Lorsque vous exécutez votre application spark au-dessus de HDFS, selon Sandy Ryza

J'ai remarqué que le client HDFS rencontrait des problèmes avec des tonnes de threads simultanés. Une approximation approximative est qu’au plus cinq tâches par exécuteur peuvent atteindre un débit d’écriture total, il est donc bon de maintenir le nombre de cœurs par exécuteur au-dessous de ce nombre.

Je pense donc que votre première configuration est plus lente que la troisième, à cause du mauvais débit d'E/S HDFS

14
tgbaggio

Je n'ai pas joué avec ces paramètres moi-même, donc ce ne sont que des spéculations, mais si nous considérons ce problème comme des cœurs et des threads normaux dans un système distribué, vous pouvez utiliser dans votre cluster jusqu'à 12 cœurs (machines 4 * 3) et 24 threads. (8 * 3 machines). Dans vos deux premiers exemples, vous attribuez à votre travail un nombre suffisant de cœurs (espace de calcul potentiel), mais le nombre de threads (travaux) à exécuter sur ces cœurs est tellement limité que vous ne pouvez pas utiliser une grande partie de la puissance de traitement allouée. et donc le travail est plus lent même s'il y a plus de ressources de calcul allouées.

vous avez mentionné que votre préoccupation se situait dans la phase de mélange - bien qu’il soit agréable de limiter les frais généraux lors de la phase de mélange, il est généralement beaucoup plus important d’utiliser la parallélisation du cluster. Pensez au cas extrême - un programme à un seul thread avec zéro shuffle.

11
pwilmot

Réponse courte : Je pense que tgbaggio a raison. Vous atteignez les limites de débit HDFS de vos exécuteurs.

Je pense que la réponse ici peut être un peu plus simple que certaines des recommandations ici.

L'indice pour moi est dans le graphe de réseau de cluster. Pour l'exécution 1, l'utilisation est stable à environ 50 M octets/s. Pour l'exécution 3, l'utilisation régulière est doublée, environ 100 M octets/s.

De l'article de blog de cloudera partagé par DzOrd , vous pouvez voir cette citation importante:

J'ai remarqué que le client HDFS rencontrait des problèmes avec des tonnes de threads simultanés. Une hypothèse approximative est qu’au plus cinq tâches par exécuteur peuvent atteindre un débit d’écriture total. Il est donc judicieux de maintenir le nombre de cœurs par exécuteur en dessous de ce nombre.

Faisons donc quelques calculs pour voir quelle performance nous attendons si cela est vrai.


Run 1: 19 Go, 7 cœurs, 3 exécuteurs

  • 3 exécuteurs x 7 threads = 21 threads
  • avec 7 cœurs par exécuteur, nous nous attendons à une limitation de IO à HDFS (maximum de ~ 5 cœurs)
  • débit effectif ~ = 3 exécuteurs x 5 threads = 15 threads

Run 3: 4 Go, 2 cœurs, 12 exécuteurs

  • 2 exécuteurs x 12 threads = 24 threads
  • 2 cœurs par exécuteur, donc le débit hdfs est correct
  • débit effectif ~ = 12 exécuteurs x 2 threads = 24 threads

Si le travail est limité à 100% par la simultanéité (nombre de threads). Nous nous attendions à ce que le temps d'exécution soit parfaitement inversement corrélé au nombre de threads.

ratio_num_threads = nthread_job1 / nthread_job3 = 15/24 = 0.625
inv_ratio_runtime = 1/(duration_job1 / duration_job3) = 1/(50/31) = 31/50 = 0.62

Donc, ratio_num_threads ~= inv_ratio_runtime, et il semble que notre réseau soit limité.

Ce même effet explique la différence entre Run 1 et Run 2.


Run 2: 19 Go, 4 cœurs, 3 exécuteurs

  • 3 exécuteurs x 4 threads = 12 threads
  • avec 4 cœurs par exécuteur, ok IO à HDFS
  • débit effectif ~ = 3 exécuteurs x 4 threads = 12 threads

Comparer le nombre de threads effectifs et le temps d'exécution:

ratio_num_threads = nthread_job2 / nthread_job1 = 12/15 = 0.8
inv_ratio_runtime = 1/(duration_job2 / duration_job1) = 1/(55/50) = 50/55 = 0.91

Ce n'est pas aussi parfait que la dernière comparaison, mais nous constatons toujours une baisse similaire des performances lorsque nous perdons des threads.

Passons maintenant au dernier point: pourquoi avons-nous de meilleures performances avec plus de threads, en particulier? plus de threads que le nombre de processeurs?

Dans cet article de Rob Pike, nous trouvons une bonne explication de la différence entre le parallélisme (ce que nous obtenons en divisant des données sur plusieurs processeurs) et la concurrence (ce que nous obtenons lorsque nous utilisons plusieurs threads pour travailler sur un seul processeur): - La simultanéité n'est pas un parallélisme .

En bref, si un travail Spark interagit avec un système de fichiers ou un réseau, le processeur attend beaucoup de son temps pour communiquer avec ces interfaces sans passer beaucoup de temps à "travailler". En donnant à ces processeurs plus d'une tâche à la fois, ils passent moins de temps à attendre et plus de temps à travailler et vous obtenez de meilleures performances.

4
turtlemonvh

Parmi les excellentes ressources disponibles sur page du paquetage Sparklyr de RStudio :

DÉFINITIONS DE L'ÉTINCELLE :

Il peut être utile de fournir quelques définitions simples pour la nomenclature Spark:

Noeud : un serveur

Noeud de travail : serveur faisant partie du cluster et disponible pour exécuter Spark travaux.

Noeud principal : serveur qui coordonne les noeuds de travail.

Exécuteur : Une sorte de machine virtuelle à l'intérieur d'un nœud. Un Node peut avoir plusieurs exécuteurs.

Nœud du pilote : Le Node qui initie la session Spark. Généralement, ce sera le serveur sur lequel se trouve sparklyr.

Pilote (exécuteur) : Le pilote Node apparaît également dans la liste des exécuteurs.

3
d8aninja

Il y a un petit problème dans les deux premières configurations, je pense. Les concepts de fils et de noyaux sont les suivants. Le concept de threading est que si les cœurs sont idéaux, utilisez-le pour traiter les données. La mémoire n'est donc pas pleinement utilisée dans les deux premiers cas. Si vous voulez évaluer cet exemple, choisissez les machines qui ont plus de 10 cœurs sur chaque machine. Ensuite, faites le repère.

Mais ne donnez pas plus de 5 cœurs par exécuteur, il y aura un goulot d'étranglement sur les performances d'E/S.

Donc, les meilleures machines pour faire ce point de repère pourraient être les nœuds de données qui ont 10 cœurs.

Spécifications de la machine du noeud de données: CPU: Core i7-4790 (nb de cœurs: 10, nb de threads: 20) RAM: 32 Go (8 Go x 4) Disque dur: 8 To (2 To x 4)

0
Achyuth

L'allocation dynamique Spark donne de la flexibilité et alloue les ressources de manière dynamique. Dans ce nombre de min et max exécuteurs peuvent être donnés. Il est également possible d'indiquer le nombre d'exécuteurs qui doivent être lancés au démarrage de l'application.

Lire ci-dessous sur le même:

http://spark.Apache.org/docs/latest/configuration.html#dynamic-allocation

0
Harikrishnan Ck

Je pense que l'une des principales raisons est la localité. La taille de votre fichier d’entrée est de 165 G, les blocs associés au fichier sont certainement répartis sur plusieurs DataNodes, de nombreux exécutants pouvant éviter la copie réseau.

Essayez de définir exécutateur num nombre égal de blocs, je pense que peut être plus rapide.

0
zwb