-
Notifications
You must be signed in to change notification settings - Fork 29k
SPARK-1706: Allow multiple executors per worker in Standalone mode #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
b34ec0c
a5d629a
a26096d
e5efabb
ec7d421
5b81466
19d3da7
0b64fea
35c462c
387f4ec
f64a28d
878402c
497ec2c
2c2bcc5
ff011e2
4cf61f1
63b3df9
f595bd6
d9c1685
f035423
12a1b32
2eeff77
45967b4
b8ca561
940cb42
fbeb7e5
6dee808
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -533,11 +533,14 @@ private[master] class Master( | |
| } | ||
|
|
||
| /** | ||
| * The resource allocator spread out each app among all the workers until it has all its cores in | ||
| * spreadOut mode otherwise packs each app into as few workers as possible until it has assigned | ||
| * all its cores. User can define spark.deploy.maxCoresPerExecutor per application to | ||
| * limit the maximum number of cores to allocate to each executor on each worker; if the parameter | ||
| * is not defined, then only one executor will be launched on a worker. | ||
| * Schedule executors to be launched on the workers.There are two modes of launching executors. | ||
| * The first attempts to spread out an application's executors on as many workers as possible, | ||
| * while the second does the opposite (i.e. launch them on as few workers as possible). The former | ||
| * is usually better for data locality purposes and is the default. The number of cores assigned | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should split on this sentence ("The number of cores...") to form a new paragraph. Right now it's one huge chunk of text |
||
| * to each executor is configurable. When this is explicitly set, multiple executors from the same | ||
| * application may be launched on the same worker if the worker has enough cores and memory. | ||
| * Otherwise, each executor grabs all the cores available on the worker by default, in which case | ||
| * only one executor may be launched on each worker. | ||
| */ | ||
| private def startExecutorsOnWorkers(): Unit = { | ||
| // Right now this is a very simple FIFO scheduler. We keep trying to fit in the first app | ||
|
|
@@ -546,7 +549,9 @@ private[master] class Master( | |
| // Try to spread out each app among all the workers, until it has all its cores | ||
| for (app <- waitingApps if app.coresLeft > 0) { | ||
| val usableWorkers = workers.toArray.filter(_.state == WorkerState.ALIVE) | ||
| .filter(canUse(app, _)).sortBy(_.coresFree).reverse | ||
| .filter(worker => worker.memoryFree >= app.desc.memoryPerExecutorMB && | ||
| worker.coresFree > 0) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you technically don't need the cores check here, since we already check in L551
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I replaced it with 'worker.coresFree >= app.desc.coresPerExecutor.getOrElse(0)', so that we do not need to run the following allocation algorithm for the case I mentioned above
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, this predicate is actually not needed because we handle it correctly in the line I pointed out earlier. But not a big deal, we can just leave it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm....if we remove this, in the case above, the user prefers 3 cores per executor and all workers have at most 2 cores, though we will not allocate anything to the worker, we still generate a |
||
| .sortBy(_.coresFree).reverse | ||
| val numUsable = usableWorkers.length | ||
| val assigned = new Array[Int](numUsable) // Number of cores to give on each node | ||
| var toAssign = math.min(app.coresLeft, usableWorkers.map(_.coresFree).sum) | ||
|
|
@@ -566,15 +571,16 @@ private[master] class Master( | |
| } else { | ||
| // Pack each app into as few workers as possible until we've assigned all its cores | ||
| for (worker <- workers if worker.coresFree > 0 && worker.state == WorkerState.ALIVE) { | ||
| for (app <- waitingApps if app.coresLeft > 0) { | ||
| allocateWorkerResourceToExecutors(app, app.coresLeft, worker) | ||
| for (app <- waitingApps if app.coresLeft > 0 && | ||
| worker.memoryFree >= app.desc.memoryPerExecutorMB) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to check this again here... we already check this in |
||
| allocateWorkerResourceToExecutors(app, app.coresLeft, worker) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * allocate resources in a certain worker to one or more executors | ||
| * Allocate a worker's resources to one or more executors. | ||
| * @param app the info of the application which the executors belong to | ||
| * @param coresToAllocate cores on this worker to be allocated to this application | ||
| * @param worker the worker info | ||
|
|
@@ -583,20 +589,24 @@ private[master] class Master( | |
| app: ApplicationInfo, | ||
| coresToAllocate: Int, | ||
| worker: WorkerInfo): Unit = { | ||
| if (canUse(app, worker)) { | ||
| val memoryPerExecutor = app.desc.memoryPerExecutorMB | ||
| val coresPerExecutor = app.desc.coresPerExecutor.getOrElse(coresToAllocate) | ||
| var coresLeft = coresToAllocate | ||
| while (coresLeft >= coresPerExecutor && worker.memoryFree >= memoryPerExecutor) { | ||
| val exec = app.addExecutor(worker, coresPerExecutor) | ||
| coresLeft -= coresPerExecutor | ||
| launchExecutor(worker, exec) | ||
| app.state = ApplicationState.RUNNING | ||
| } | ||
| val memoryPerExecutor = app.desc.memoryPerExecutorMB | ||
| val coresPerExecutor = app.desc.coresPerExecutor.getOrElse(coresToAllocate) | ||
| var coresLeft = coresToAllocate | ||
| while (coresLeft >= coresPerExecutor && worker.memoryFree >= memoryPerExecutor) { | ||
| val exec = app.addExecutor(worker, coresPerExecutor) | ||
| coresLeft -= coresPerExecutor | ||
| launchExecutor(worker, exec) | ||
| app.state = ApplicationState.RUNNING | ||
| } | ||
| } | ||
|
|
||
| private def startDriversOnWorkers(): Unit = { | ||
| /** | ||
| * Schedule the currently available resources among waiting apps. This method will be called | ||
| * every time a new app joins or resource availability changes. | ||
| */ | ||
| private def schedule(): Unit = { | ||
| if (state != RecoveryState.ALIVE) { return } | ||
| // start in-cluster drivers, they take strict precedence over applications | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment doesn't make much sense. Can you just replace it with |
||
| val shuffledWorkers = Random.shuffle(workers) // Randomization helps balance drivers | ||
| for (worker <- shuffledWorkers if worker.state == WorkerState.ALIVE) { | ||
| for (driver <- waitingDrivers) { | ||
|
|
@@ -606,21 +616,11 @@ private[master] class Master( | |
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Schedule the currently available resources among waiting apps. This method will be called | ||
| * every time a new app joins or resource availability changes. | ||
| */ | ||
| private def schedule(): Unit = { | ||
| if (state != RecoveryState.ALIVE) { return } | ||
| // start in-cluster drivers, they take strict precedence over applications | ||
| startDriversOnWorkers() | ||
| // start executors | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this comment, as it doesn't convey any information |
||
| startExecutorsOnWorkers() | ||
| } | ||
|
|
||
| def launchExecutor(worker: WorkerInfo, exec: ExecutorDesc): Unit = { | ||
| def launchExecutor(worker: WorkerInfo, exec: ExecutorDesc): Unit = { | ||
| logInfo("Launching executor " + exec.fullId + " on worker " + worker.id) | ||
| worker.addExecutor(exec) | ||
| worker.actor ! LaunchExecutor(masterUrl, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you break "There are two modes of..." into a new paragraph?