-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make sandbox symlink forest deletions asynchronous.
Each sandbox action runs within a symlink forest that exists in a separate subtree because we use a unique identifier for those subtrees. Therefore it is unnecessary to delete those trees in the critical path. Tree deletions can be very expensive, especially on macOS, so make them asynchronous if --experimental_sandbox_async_tree_delete_idle_threads is given. When this flag is not zero, Bazel will schedule all deletions on a separate low-priority thread while the build is running, and will then use the requested number of threads once the build is done to quickly catch up with an still-ongoing deletions. For a large iOS build, this cuts down clean build times with sandboxed enabled significantly. Helps more on machines with more cores: * On a Mac Pro 2013, the improvement is almost 20%: standalone: mean 2746.33, median 2736.00, stddev 33.07 sandboxed-async: mean 4394.67, median 4393.00, stddev 33.09 sandboxed-sync: mean 5284.33, median 5288.00, stddev 20.17 * On a MacBook Pro 2015, we see a more modest 10% improvement: standalone: mean 3418.33, median 3422.00, stddev 7.41 sandboxed-async: mean 5090.00, median 5086.00, stddev 40.92 sandboxed-sync: mean 5694.67, median 5700.00, stddev 37.75 Partially addresses #7527. RELNOTES: None. PiperOrigin-RevId: 243805556
- Loading branch information
1 parent
5074514
commit 16af94c
Showing
22 changed files
with
415 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
src/main/java/com/google/devtools/build/lib/exec/TreeDeleter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright 2019 The Bazel Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package com.google.devtools.build.lib.exec; | ||
|
||
import com.google.devtools.build.lib.vfs.Path; | ||
import java.io.IOException; | ||
|
||
/** Interface to execute tree deletions under different scheduling policies. */ | ||
public interface TreeDeleter { | ||
|
||
/** | ||
* Deletes a tree. | ||
* | ||
* <p>Note that depending on the scheduling policy implemented by this tree deleter, errors may | ||
* not be reported even if they happen. For example, if deletions are asynchronous, there is no | ||
* way to capture their errors. | ||
* | ||
* @param path the tree to be deleted | ||
* @throws IOException if there are problems deleting the tree | ||
*/ | ||
void deleteTree(Path path) throws IOException; | ||
|
||
/** | ||
* Deletes the contents of a tree, but not the top-level directory. | ||
* | ||
* <p>Note that depending on the scheduling policy implemented by this tree deleter, errors may | ||
* not be reported even if they happen. For example, if deletions are asynchronous, there is no | ||
* way to capture their errors. | ||
* | ||
* @param path the tree to be deleted | ||
* @throws IOException if there are problems deleting the tree | ||
*/ | ||
void deleteTreesBelow(Path path) throws IOException; | ||
|
||
/** Shuts down the tree deleter and cleans up pending operations, if any. */ | ||
void shutdown(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
src/main/java/com/google/devtools/build/lib/sandbox/AsynchronousTreeDeleter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright 2019 The Bazel Authors. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package com.google.devtools.build.lib.sandbox; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
import static com.google.common.base.Preconditions.checkState; | ||
|
||
import com.google.common.util.concurrent.ThreadFactoryBuilder; | ||
import com.google.devtools.build.lib.exec.TreeDeleter; | ||
import com.google.devtools.build.lib.vfs.Path; | ||
import java.io.IOException; | ||
import java.util.concurrent.LinkedBlockingQueue; | ||
import java.util.concurrent.ThreadFactory; | ||
import java.util.concurrent.ThreadPoolExecutor; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.logging.Logger; | ||
import javax.annotation.Nullable; | ||
|
||
/** | ||
* Executes file system tree deletions asynchronously. | ||
* | ||
* <p>The number of threads used to process the backlog of tree deletions can be configured at any | ||
* time via {@link #setThreads(int)}. While a build is running, this number should be low to not use | ||
* precious resources that could otherwise be used for the build itself. But when the build is | ||
* finished, this number should be raised to quickly go through any pending deletions. | ||
*/ | ||
class AsynchronousTreeDeleter implements TreeDeleter { | ||
|
||
private static final Logger logger = Logger.getLogger(TreeDeleter.class.getName()); | ||
|
||
/** Thread pool used to execute asynchronous tree deletions; null in synchronous mode. */ | ||
@Nullable private ThreadPoolExecutor service; | ||
|
||
/** Constructs a new asynchronous tree deleter backed by just one thread. */ | ||
AsynchronousTreeDeleter() { | ||
logger.info("Starting async tree deletion pool with 1 thread"); | ||
|
||
ThreadFactory threadFactory = | ||
new ThreadFactoryBuilder() | ||
.setNameFormat("tree-deleter") | ||
.setDaemon(true) | ||
.setPriority(Thread.MIN_PRIORITY) | ||
.build(); | ||
|
||
service = | ||
new ThreadPoolExecutor( | ||
1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), threadFactory); | ||
} | ||
|
||
/** | ||
* Resizes the thread pool to the given number of threads. | ||
* | ||
* <p>If the pool of active threads is larger than the requested number of threads, the resize | ||
* will progressively happen as those active threads become inactive. If the requested size is | ||
* zero, this will wait for all pending deletions to complete. | ||
* | ||
* @param threads desired number of threads, or 0 to go back to synchronous deletion | ||
*/ | ||
void setThreads(int threads) { | ||
checkState(threads > 0, "Use SynchronousTreeDeleter if no async behavior is desired"); | ||
logger.info("Resizing async tree deletion pool to " + threads + " threads"); | ||
checkNotNull(service, "Cannot call setThreads after shutdown").setMaximumPoolSize(threads); | ||
} | ||
|
||
@Override | ||
public void deleteTree(Path path) { | ||
checkNotNull(service, "Cannot call deleteTree after shutdown") | ||
.execute( | ||
() -> { | ||
try { | ||
path.deleteTree(); | ||
} catch (IOException e) { | ||
logger.warning("Failed to delete tree " + path + " asynchronously: " + e); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public void deleteTreesBelow(Path path) { | ||
checkNotNull(service, "Cannot call deleteTree after shutdown") | ||
.execute( | ||
() -> { | ||
try { | ||
path.deleteTreesBelow(); | ||
} catch (IOException e) { | ||
logger.warning("Failed to delete contents of " + path + " asynchronously: " + e); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public void shutdown() { | ||
if (service != null) { | ||
logger.info("Finishing " + service.getTaskCount() + " pending async tree deletions"); | ||
service.shutdown(); | ||
service = null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.