Apache Spark: il numero di core rispetto al numero di esecutori


195

Sto cercando di capire la relazione tra il numero di core e il numero di esecutori durante l'esecuzione di un processo Spark su YARN.

L'ambiente di test è il seguente:

  • Numero di nodi di dati: 3
  • Specifiche della macchina del nodo dati:
    • CPU: Core i7-4790 (N. di core: 4, N. di thread: 8)
    • RAM: 32 GB (8 GB x 4)
    • HDD: 8 TB (2 TB x 4)
  • Rete: 1Gb

  • Versione Spark: 1.0.0

  • Versione Hadoop: 2.4.0 (Hortonworks HDP 2.1)

  • Flusso di lavoro Spark: sc.textFile -> filtro -> mappa -> filtro -> mapToPair -> reduceByKey -> mappa -> saveAsTextFile

  • Dati in ingresso

    • Tipo: file di testo singolo
    • Dimensioni: 165 GB
    • Numero di linee: 454.568.833
  • Produzione

    • Numero di righe dopo il secondo filtro: 310.640.717
    • Numero di righe del file dei risultati: 99.848.268
    • Dimensione del file dei risultati: 41 GB

Il lavoro è stato eseguito con le seguenti configurazioni:

  1. --master yarn-client --executor-memory 19G --executor-cores 7 --num-executors 3 (esecutori per nodo dati, utilizza tanto quanto i core)

  2. --master yarn-client --executor-memory 19G --executor-cores 4 --num-executors 3 (Numero di core ridotti)

  3. --master yarn-client --executor-memory 4G --executor-cores 2 --num-executors 12 (meno core, più esecutore)

Tempi trascorsi:

  1. 50 min 15 sec

  2. 55 min 48 sec

  3. 31 min 23 sec

Con mia sorpresa, (3) è stato molto più veloce.
Ho pensato che (1) sarebbe stato più veloce, dato che ci sarebbero state meno comunicazioni tra gli esecutori quando si mescolavano.
Sebbene il numero di core di (1) sia inferiore a (3), il numero di core non è il fattore chiave poiché 2) ha funzionato bene.

(Seguiti aggiunti dopo la risposta di pwilmot.)

Per informazione, la cattura dello schermo del monitor delle prestazioni è la seguente:

  • Riepilogo nodo dati gangliari per (1) - il lavoro è iniziato alle 04:37.

Riepilogo nodo dati gangliari per (1)

  • Riepilogo nodo dati gangliari per (3) - il lavoro è iniziato alle 19:47. Si prega di ignorare il grafico prima di quel momento.

Riepilogo nodo dati gangliari per (3)

Il grafico si divide approssimativamente in 2 sezioni:

  • Primo: dall'inizio alla riduzioneByKey: CPU intensiva, nessuna attività di rete
  • Secondo: dopo ridurreByKey: la CPU si abbassa, l'I / O di rete è terminato.

Come mostra il grafico, (1) può utilizzare tutta la potenza della CPU che è stata fornita. Quindi, potrebbe non essere il problema del numero di thread.

Come spiegare questo risultato?


2
Ora sospetto GC ... In effetti, sull'interfaccia utente di Spark il tempo totale trascorso per GC è più lungo di 1) di 2).
zeodtr,

Perché non hai provato 3) con 19G? Potrebbe essere che limitare i lavoratori su 4G riduca l'effetto NUMA che alcuni ppl hanno posto? vale a dire che il tuo 4G si trova su uno dei 2 core assegnati al tuo flusso di lavoro e quindi c'è un rallentamento dell'i / o minore, che porta a migliori prestazioni complessive. Altrimenti penso che una domanda principale sia: quanti core / thread possono usare un singolo esecutore su un lavoratore? (Si può solo specificare il numero totale di core per un lavoratore, non per la granularità dell'esecutore)
Bacon

5
Tra l'altro ho appena controllato il codice su core / src / main / scala / org / apache / spark / deploy / worker / ExecutorRunner.scala e sembra che 1 esecutore = 1 thread di lavoro.
Bacon

un po 'in ritardo ma ecco un post su cloudera su questo argomento: blog.cloudera.com/blog/2015/03/…
Orelus,

1
A proposito, ho trovato queste informazioni in una piattaforma di diapositive cloudera slideshare.net/cloudera/… , che spiega un po 'il processo decisionale in merito a esecutori, core e memoria
Manish Sahni,

Risposte:


58

Per spero di rendere tutto questo un po 'più concreto, ecco un esempio funzionante di configurazione di un'app Spark per utilizzare il maggior numero possibile di cluster: Immagina un cluster con sei nodi che eseguono NodeManager, ognuno dotato di 16 core e 64 GB di memoria . Le capacità di NodeManager, yarn.nodemanager.resource.memory-mb e yarn.nodemanager.resource.cpu-vcores, dovrebbero probabilmente essere impostate su 63 * 1024 = 64512 (megabyte) e 15 rispettivamente. Evitiamo di allocare il 100% delle risorse ai contenitori YARN perché il nodo necessita di alcune risorse per eseguire il sistema operativo e i daemon Hadoop. In questo caso, lasciamo un gigabyte e un nucleo per questi processi di sistema. Cloudera Manager aiuta tenendo conto di questi e configurando automaticamente queste proprietà YARN.

Il primo possibile impulso sarebbe usare --num -ecutors 6 --executor-core 15 --executor-memory 63G . Tuttavia, questo è l'approccio sbagliato perché:

63 GB + l'overhead di memoria dell'esecutore non si adatta alla capacità di 63 GB dei NodeManager. Il master dell'applicazione occuperà un core su uno dei nodi, il che significa che non ci sarà spazio per un esecutore a 15 core su quel nodo. 15 core per esecutore possono portare a un throughput I / O HDFS errato.

Un'opzione migliore sarebbe quella di usare --num -ecutors 17 --executor-core 5 --executor-memory 19G . Perché?

Questa configurazione comporta tre esecutori su tutti i nodi ad eccezione di quello con AM, che avrà due esecutori. --executor-memory è stata derivata come (63/3 esecutori per nodo) = 21. 21 * 0.07 = 1.47. 21 - 1,47 ~ 19.

La spiegazione è stata data in un articolo nel blog di Cloudera, How-to: Tune Your Apache Spark Jobs (Parte 2) .


1
"Questa configurazione si traduce in tre esecutori su tutti i nodi ad eccezione di quello con AM, che avrà due esecutori". Che cosa significa questo con "--executor-cores 5"?
Derek,

Significa che ogni esecutore usa 5 core. Ogni nodo ha 3 esecutori, quindi utilizza 15 core, tranne uno dei nodi eseguirà anche il master dell'applicazione per il lavoro, quindi può ospitare solo 2 esecutori, ovvero 10 core in uso come esecutori.
Davos,

Spiegazione corretta: si noti che questo vale per i yarn.scheduler.capacity.resource-calculatordisabili, che è l'impostazione predefinita. Questo perché di default pianifica per Memoria e non per CPU.
YoYo

1
Un numero maggiore di esecutori può portare a un throughput I / O HDFS errato. Quindi se non uso affatto HDFS, in quel caso posso usare più di 5 core per esecutore?
Darshan,

Pensavo che il master dell'applicazione funzionasse su ciascun nodo. Come sopra, il che significa che ci sarebbe solo 1 Application Master per eseguire il lavoro. È corretto?
Roshan Fernando,

15

Mentre esegui la tua app spark su HDFS, secondo Sandy Ryza

Ho notato che il client HDFS ha problemi con tonnellate di thread simultanei. Un'ipotesi approssimativa è che al massimo cinque attività per esecutore possano raggiungere la piena velocità di scrittura, quindi è bene mantenere il numero di core per esecutore al di sotto di quel numero.

Quindi credo che la tua prima configurazione sia più lenta della terza a causa del cattivo throughput I / O HDFS


11

Non ho giocato con queste impostazioni da solo, quindi questa è solo una speculazione ma se pensiamo a questo problema come core e thread normali in un sistema distribuito, nel tuo cluster puoi usare fino a 12 core (macchine 4 * 3) e 24 thread (8 * 3 macchine). Nei tuoi primi due esempi stai dando al tuo lavoro un discreto numero di core (potenziale spazio di calcolo) ma il numero di thread (lavori) da eseguire su quei core è così limitato che non puoi usare gran parte della potenza di elaborazione assegnata e quindi il lavoro è più lento anche se sono allocate più risorse di calcolo.

dici che la tua preoccupazione era nella fase shuffle - mentre è bello limitare l'overhead nella fase shuffle, è generalmente molto più importante utilizzare la parallelizzazione del cluster. Pensa al caso estremo: un singolo programma thread con zero shuffle.


Grazie per la tua risposta Ma sospetto che il numero di thread non sia il problema principale. Ho aggiunto la schermata di monitoraggio. Come mostra il grafico, 1) può utilizzare tutta la potenza della CPU fornita.
zeodtr,

1
@zeodtr pwilmot è corretto: sono necessarie 2-4 attività MINIME per sfruttare tutto il potenziale dei core. In altre parole, di solito uso almeno 1000 partizioni per il mio cluster 80 core.
Samthebest,

@samthebest Quello che voglio sapere è il motivo della differenza di prestazioni tra 1) e 3). Quando guardo l'interfaccia utente Spark, entrambe eseguono 21 attività in parallelo nella sezione 2. (perché 21 invece di 24 in caso di 3) per ora non è noto) Ma, le attività per 3) sono più veloci.
zeodtr,

11

Risposta breve : penso che tgbaggio abbia ragione. Raggiungi i limiti di throughput HDFS sui tuoi esecutori.

Penso che la risposta qui possa essere un po 'più semplice di alcune raccomandazioni qui.

L'indizio per me è nel grafico della rete del cluster. Per l'esecuzione 1 l'utilizzo è costante a ~ 50 M byte / s. Per l'esecuzione 3, l'utilizzo costante è raddoppiato, circa 100 M byte / s.

Dal post sul blog cloudera condiviso da DzOrd , puoi vedere questa importante citazione:

Ho notato che il client HDFS ha problemi con tonnellate di thread simultanei. Un'ipotesi approssimativa è che al massimo cinque attività per esecutore possano raggiungere la piena velocità di scrittura, quindi è bene mantenere il numero di core per esecutore al di sotto di quel numero.

Quindi, facciamo alcuni calcoli per vedere quali prestazioni ci aspettiamo se ciò è vero.


Esegui 1: 19 GB, 7 core, 3 esecutori

  • 3 esecutori x 7 thread = 21 thread
  • con 7 core per esecutore, prevediamo un IO limitato a HDFS (massimo a ~ 5 core)
  • throughput effettivo ~ = 3 esecutori x 5 thread = 15 thread

Esegui 3: 4 GB, 2 core, 12 esecutori

  • 2 esecutori x 12 thread = 24 thread
  • 2 core per esecutore, quindi il throughput di hdfs è ok
  • throughput effettivo ~ = 12 esecutori x 2 thread = 24 thread

Se il lavoro è limitato al 100% dalla concorrenza (il numero di thread). Ci aspetteremmo che il runtime sia perfettamente inversamente correlato al numero di thread.

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

Quindi ratio_num_threads ~= inv_ratio_runtime, e sembra che siamo limitati alla rete.

Questo stesso effetto spiega la differenza tra Run 1 e Run 2.


Esegui 2: 19 GB, 4 core, 3 esecutori

  • 3 esecutori x 4 thread = 12 thread
  • con 4 core per esecutore, ok da IO a HDFS
  • throughput effettivo ~ = 3 esecutori x 4 thread = 12 thread

Confronto tra il numero di thread effettivi e il runtime:

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

Non è perfetto come l'ultimo confronto, ma vediamo ancora un calo simile delle prestazioni quando perdiamo i thread.

Ora per l'ultimo bit: perché possiamo ottenere prestazioni migliori con più thread, esp. più thread rispetto al numero di CPU?

Una buona spiegazione della differenza tra parallelismo (ciò che otteniamo dividendo i dati su più CPU) e la concorrenza (ciò che otteniamo quando utilizziamo più thread per lavorare su una singola CPU) è fornita in questo fantastico post di Rob Pike: Concurrency non è parallelismo .

La breve spiegazione è che se un processo Spark sta interagendo con un file system o una rete, la CPU trascorre molto tempo in attesa di comunicazione con quelle interfacce e non impiega molto tempo a "lavorare". Dando a quelle CPU più di 1 compito su cui lavorare alla volta, stanno spendendo meno tempo in attesa e più tempo a lavorare e vedrai prestazioni migliori.


1
Spiegazione interessante e convincente, mi chiedo se come hai pensato che l'esecutore abbia un limite di 5 compiti per raggiungere il massimo rendimento.
Dat Nguyen,

Quindi il numero 5 non è qualcosa che mi è venuta in mente: ho appena notato i segni del collo di bottiglia di IO e sono andato alla ricerca della provenienza di questi colli di bottiglia.
turtlemonvh

8

Dalle eccellenti risorse disponibili nella pagina del pacchetto Sparklyr di RStudio :

DEFINIZIONI DI SPARK :

Può essere utile fornire alcune semplici definizioni per la nomenclatura Spark:

Nodo : un server

Nodo lavoratore : un server che fa parte del cluster e sono disponibili per eseguire processi Spark

Nodo principale : il server che coordina i nodi di lavoro.

Esecutore : una sorta di macchina virtuale all'interno di un nodo. Un nodo può avere più esecutori.

Driver Node : il nodo che avvia la sessione Spark. In genere, questo sarà il server in cui si trova sparklyr.

Driver (Executor) : il nodo Driver verrà visualizzato anche nell'elenco Executor.



1

Penso che ci sia un piccolo problema nelle prime due configurazioni. I concetti di fili e core come segue. Il concetto di threading è se i core sono ideali, quindi utilizzare quel core per elaborare i dati. Quindi la memoria non viene utilizzata completamente nei primi due casi. Se si desidera contrassegnare questo esempio, scegliere le macchine con più di 10 core su ogni macchina. Quindi fai il segno di riferimento.

Ma non dare più di 5 core per esecutore ci sarà collo di bottiglia sulle prestazioni di I / O.

Quindi le macchine migliori per eseguire questa marcatura da banco potrebbero essere i nodi di dati che hanno 10 core.

Specifiche della macchina del nodo dati: CPU: Core i7-4790 (N. di core: 10, N. di thread: 20) RAM: 32 GB (8 GB x 4) HDD: 8 TB (2 TB x 4)


0

Penso che uno dei motivi principali sia la località. La dimensione del file di input è 165G, i blocchi relativi al file sono sicuramente distribuiti su più DataNode, più esecutori possono evitare la copia di rete.

Prova a impostare il numero esatto di blocchi per numero di esecutori, penso che possa essere più veloce.

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.