spark-submit --class <CLASS_NAME> --num-executors ? --executor-cores ? --executor-memory ? ....
--execuor-coresspark config params for your cluster?
Two things to make note of from this picture:
Full memory requested to yarn per executor = spark-executor-memory + spark.yarn.executor.memoryOverhead. spark.yarn.executor.memoryOverhead = Max(384MB, 7% of spark.executor-memory)
So, if we request 20GB per executor, AM will actually get 20GB + memoryOverhead = 20 + 7% of 20GB = ~23GB memory for us.
Now, let’s consider a 10 node cluster with following config and analyse different possibilities of executors-core-memory distribution:
**Cluster Config:** 10 Nodes 16 cores per Node 64GB RAM per Node
Tiny executors essentially means one executor per core. Following table depicts the values of our spar-config params with this approach:
- `--num-executors` = `In this approach, we'll assign one executor per core` = `total-cores-in-cluster` = `num-cores-per-node * total-nodes-in-cluster` = 16 x 10 = 160 - `--executor-cores` = 1 (one executor per core) - `--executor-memory` = `amount of memory per executor` = `mem-per-node/num-executors-per-node` = 64GB/16 = 4GB
Analysis: With only one executor per core, as we discussed above, we’ll not be able to take advantage of running multiple tasks in the same JVM. Also, shared/cached variables like broadcast variables and accumulators will be replicated in each core of the nodes which is 16 times. Also, we are not leaving enough memory overhead for Hadoop/Yarn daemon processes and we are not counting in ApplicationManager. NOT GOOD!
Fat executors essentially means one executor per node. Following table depicts the values of our spark-config params with this approach:
- `--num-executors` = `In this approach, we'll assign one executor per node` = `total-nodes-in-cluster` = 10 - `--executor-cores` = `one executor per node means all the cores of the node are assigned to one executor` = `total-cores-in-a-node` = 16 - `--executor-memory` = `amount of memory per executor` = `mem-per-node/num-executors-per-node` = 64GB/1 = 64GB
Analysis: With all 16 cores per executor, apart from ApplicationManager and daemon processes are not counted for, HDFS throughput will hurt and it’ll result in excessive garbage results. Also,NOT GOOD!
According to the recommendations which we discussed above:
--executor-cores = 5(for good HDFS throughput)
Number of available executors = (total cores/num-cores-per-executor) = 150/5 = 30
--executor-memory= 21 - 3 = 18GB
So, recommended config is: 29 executors, 18GB memory each and 5 cores each!!
Analysis: It is obvious as to how this third approach has found right balance between Fat vs Tiny approaches. Needless to say, it achieved parallelism of a fat executor and best throughputs of a tiny executor!!
--executor-memory.. these three params play a very important role in spark performance as they control the amount of CPU & memory your spark application gets. This makes it very crucial for users to understand the right way to configure them. Hope this blog helped you in getting that perspective…