Come sintonizzare il numero di executor, i core e la memoria dell’esecutore?

Dove inizi a sintonizzare i parametri sopra menzionati. Cominciamo con la memoria dell’esecutore e otteniamo il numero di esecutori, oppure iniziamo con i core e otteniamo il numero dell’esecutore. Ho seguito il link . Tuttavia ha ottenuto un’idea di alto livello, ma non è ancora sicuro su come o dove iniziare e arrivare ad una conclusione finale.

La seguente risposta copre i 3 aspetti principali menzionati nel titolo: numero di esecutori, memoria dell’esecutore e numero di core. Ci possono essere altri parametri come la memoria del driver e altri che non ho affrontato in questa risposta, ma vorrei aggiungere in un prossimo futuro.

Hardware Case 1: 6 nodes e ogni nodo 16 core, 64 GB di RAM

Ogni esecutore è un’istanza JVM. Quindi possiamo avere più esecutori in un singolo nodo

I primi 1 core e 1 GB sono necessari per OS e Hadoop Daemon, quindi sono disponibili 15 core, 63 GB di RAM per ciascun nodo

Inizia con come scegliere il numero di core :

Number of cores = Concurrent tasks as executor can run So we might think, more concurrent tasks for each executor will give better performance. But research shows that any application with more than 5 concurrent tasks, would lead to bad show. So stick this to 5. This number came from the ability of executor and not from how many cores a system has. So the number 5 stays same even if you have double(32) cores in the CPU. 

Numero di esecutori:

 Coming back to next step, with 5 as cores per executor, and 15 as total available cores in one Node(CPU) - we come to 3 executors per node. So with 6 nodes, and 3 executors per node - we get 18 executors. Out of 18 we need 1 executor (java process) for AM in YARN we get 17 executors This 17 is the number we give to spark using --num-executors while running from spark-submit shell command 

Memoria per ogni esecutore:

 From above step, we have 3 executors per node. And available RAM is 63 GB So memory for each executor is 63/3 = 21GB. However small overhead memory is also needed to determine the full memory request to YARN for each executor. Formula for that over head is max(384, .07 * spark.executor.memory) Calculating that overhead - .07 * 21 (Here 21 is calculated as above 63/3) = 1.47 Since 1.47 GB > 384 MB, the over head is 1.47. Take the above from each 21 above => 21 - 1.47 ~ 19 GB So executor memory - 19 GB 

Numeri finali – Esecutori – 17, Core 5, Executor Memory – 19 GB


Hardware Case 2: stesso nodo 6, 32 core, 64 GB

5 è lo stesso per una buona concorrenza

Numero di esecutori per ciascun nodo = 32/5 ~ 6

Quindi gli esecutori totali = 6 * 6 nodes = 36. Quindi il numero finale è 36 – 1 per AM = 35

La memoria esecutore è: 6 esecutori per ogni nodo. 63/6 ~ 10. Sopra la testa è .07 * 10 = 700 MB. Quindi arrotondando a 1 GB come overhead, otteniamo 10-1 = 9 GB

Numeri finali – Executor – 35, Core 5, Executor Memory – 9 GB


Caso 3

Gli scenari di cui sopra iniziano con l’accettazione del numero di core come fisso e passando a # di esecutori e memoria.

Ora per il primo caso, se pensiamo di non aver bisogno di 19 GB, e solo 10 GB sono sufficienti, seguiranno i numeri:

core 5 # di esecutori per ogni nodo = 3

In questa fase, questo porterebbe a 21, e poi 19 come da nostro primo calcolo. Ma dal momento che pensavamo che 10 fosse ok (supponiamo un piccolo overhead), allora non possiamo convertire # di executor per nodo su 6 (come 63/10). Con 6 esecutori per nodo e 5 core diventa fino a 30 core per nodo, quando abbiamo solo 16 core. Quindi abbiamo anche bisogno di cambiare il numero di core per ogni esecutore.

Quindi, calcolando di nuovo,

Il numero magico 5 arriva a 3 (qualsiasi numero inferiore o uguale a 5). Quindi con 3 core e 15 core disponibili – otteniamo 5 esecutori per nodo. Quindi (5 * 6 -1) = 29 esecutori

Quindi la memoria è 63/5 ~ 12. Overhead è 12 * .07 = .84 Quindi la memoria dell’esecutore è 12 – 1 GB = 11 GB

I numeri finali sono 29 esecutori, 3 core, la memoria dell’esecutore è di 11 GB


Allocazione dynamic:

Nota: limite superiore per il numero di esecutori se l’allocazione dynamic è abilitata. Quindi questo dice che l’applicazione spark può consumare tutte le risorse se necessario. Quindi, in un cluster in cui sono in esecuzione altre applicazioni e hanno bisogno di core per eseguire le attività, assicurati di farlo a livello di cluster. Voglio dire, puoi assegnare un numero specifico di core per YARN in base all’accesso dell’utente. Quindi puoi creare spark_user potrebbe essere e quindi dare core (min / max) per quell’utente. Questi limiti sono per la condivisione tra la scintilla e altre applicazioni che girano su YARN.

spark.dynamicAllocation.enabled – Quando è impostato su true – Non è necessario menzionare gli executors. Il motivo è qui sotto:

Il numero di parametri statici che forniamo a spark-submit è per l’intera durata del lavoro. Tuttavia, se l’allocazione dynamic viene visualizzata, ci saranno diverse fasi come

Che cosa iniziare con:

Numero iniziale di esecutori ( spark.dynamicAllocation.initialExecutors ) per iniziare

Quanti :

Quindi in base al carico (attività in sospeso) quante richieste. Questo alla fine sarebbe il numero di ciò che diamo a scintilla-presentare in modo statico. Quindi, una volta impostati i numeri iniziali dell’esecutore, passiamo ai numeri min ( spark.dynamicAllocation.minExecutors ) e max ( spark.dynamicAllocation.maxExecutors ).

Quando chiedere o dare:

Quando chiediamo i nuovi esecutori ( spark.dynamicAllocation.schedulerBacklogTimeout ) – Ci sono state attività in sospeso per questa lunga durata. quindi richiesta il numero di esecutori richiesti in ciascun round aumenta in maniera esponenziale rispetto al round precedente. Ad esempio, un’applicazione aggiungerà 1 esecutore nel primo round e poi 2, 4, 8 e così via gli esecutori nei round successivi. In un punto specifico, il massimo sopra entra in figura

quando diamo un esecutore ( spark.dynamicAllocation.executorIdleTimeout ) –

Per favore correggimi se ho perso qualcosa. Quanto sopra è la mia comprensione basata sul blog che ho condiviso in questione e alcune risorse online. Grazie.

Riferimenti:

Inoltre, dipende dal tuo caso d’uso, un importante parametro di configurazione è:

spark.memory.fraction (frazione di (spazio heap – 300 MB) utilizzato per l’esecuzione e l’archiviazione) da http://spark.apache.org/docs/latest/configuration.html#memory-management .

Se non usi cache / persist, impostalo su 0.1 in modo da avere tutta la memoria per il tuo programma.

Se usi cache / persist, puoi controllare la memoria presa da:

 sc.getExecutorMemoryStatus.map(a => (a._2._1 - a._2._2)/(1024.0*1024*1024)).sum 

Leggi dati da HDFS o da HTTP?

Di nuovo, una sintonizzazione dipende dal tuo caso d’uso.