Add options to control executer threads and CPUs
* D8/R8/L8 gets the --thread-count option (currently not documented) to
control the threads created in the Java Executor
* run_on_app.py gets the option --cpu-list to pin execution to a fixed
set of cpus
Change-Id: I288bd8024b3c6b2acd5eebb284447ee5bd3456be
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 1431a9a..80dcf13 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -46,6 +47,7 @@
private final BiPredicate<String, Long> dexClassChecksumFilter;
private final List<AssertionsConfiguration> assertionsConfiguration;
private final List<Consumer<Inspector>> outputInspections;
+ private int threadCount;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -60,6 +62,7 @@
dexClassChecksumFilter = (name, checksum) -> true;
assertionsConfiguration = new ArrayList<>();
outputInspections = null;
+ threadCount = ThreadUtils.NOT_SPECIFIED;
}
BaseCompilerCommand(
@@ -74,7 +77,8 @@
boolean includeClassesChecksum,
BiPredicate<String, Long> dexClassChecksumFilter,
List<AssertionsConfiguration> assertionsConfiguration,
- List<Consumer<Inspector>> outputInspections) {
+ List<Consumer<Inspector>> outputInspections,
+ int threadCount) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -89,6 +93,7 @@
this.dexClassChecksumFilter = dexClassChecksumFilter;
this.assertionsConfiguration = assertionsConfiguration;
this.outputInspections = outputInspections;
+ this.threadCount = threadCount;
}
/**
@@ -155,6 +160,11 @@
return Collections.unmodifiableList(outputInspections);
}
+ /** Get the number of threads to use for the compilation. */
+ public int getThreadCount() {
+ return threadCount;
+ }
+
Reporter getReporter() {
return reporter;
}
@@ -178,6 +188,7 @@
private CompilationMode mode;
private int minApiLevel = 0;
+ private int threadCount = ThreadUtils.NOT_SPECIFIED;
protected DesugarState desugarState = DesugarState.ON;
private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>();
private boolean includeClassesChecksum = false;
@@ -503,6 +514,20 @@
return self();
}
+ /** Set the number of threads to use for the compilation */
+ B setThreadCount(int threadCount) {
+ if (threadCount <= 0) {
+ getReporter().error("Invalid threadCount: " + threadCount);
+ } else {
+ this.threadCount = threadCount;
+ }
+ return self();
+ }
+
+ int getThreadCount() {
+ return threadCount;
+ }
+
/** Encodes the checksums into the dex output. */
public boolean getIncludeClassesChecksum() {
return includeClassesChecksum;
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index dbfe6b7..410441b 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -12,10 +12,14 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.function.Consumer;
public class BaseCompilerCommandParser<
C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>> {
+ protected static final String MIN_API_FLAG = "--min-api";
+ protected static final String THREAD_COUNT_FLAG = "--thread-count";
+
static final Iterable<String> ASSERTIONS_USAGE_MESSAGE =
Arrays.asList(
" --force-enable-assertions[:[<class name>|<package name>...]]",
@@ -32,19 +36,20 @@
" # is the default handling of javac assertion code when",
" # generating class file format.");
- void parseMinApi(B builder, String minApiString, Origin origin) {
- int minApi;
+ void parsePositiveIntArgument(
+ B builder, String flag, String argument, Origin origin, Consumer<Integer> setter) {
+ int value;
try {
- minApi = Integer.parseInt(minApiString);
+ value = Integer.parseInt(argument);
} catch (NumberFormatException e) {
- builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ builder.error(new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
- if (minApi < 1) {
- builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString, origin));
+ if (value < 1) {
+ builder.error(new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
- builder.setMinApiLevel(minApi);
+ setter.accept(value);
}
private static String PACKAGE_ASSERTION_POSTFIX = "...";
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 32e653c..92f194a 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
@@ -226,6 +227,7 @@
libraryConfiguration,
getAssertionsConfiguration(),
getOutputInspections(),
+ getThreadCount(),
factory);
}
}
@@ -295,6 +297,7 @@
DesugaredLibraryConfiguration libraryConfiguration,
List<AssertionsConfiguration> assertionsConfiguration,
List<Consumer<Inspector>> outputInspections,
+ int threadCount,
DexItemFactory factory) {
super(
inputApp,
@@ -308,7 +311,8 @@
encodeChecksum,
dexClassChecksumFilter,
assertionsConfiguration,
- outputInspections);
+ outputInspections,
+ threadCount);
this.intermediate = intermediate;
this.desugarGraphConsumer = desugarGraphConsumer;
this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -380,6 +384,9 @@
internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+
return internal;
}
}
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index a6827d3..e9df325 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -29,10 +29,11 @@
"--output",
"--lib",
"--classpath",
- "--min-api",
+ MIN_API_FLAG,
"--main-dex-list",
"--main-dex-list-output",
- "--desugared-lib");
+ "--desugared-lib",
+ THREAD_COUNT_FLAG);
private static final String APK_EXTENSION = ".apk";
private static final String JAR_EXTENSION = ".jar";
@@ -121,7 +122,10 @@
" # <file> must be an existing directory or a zip file.",
" --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
" --classpath <file> # Add <file> as a classpath resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
+ " "
+ + MIN_API_FLAG
+ + " <number> "
+ + "# Minimum Android API level compatibility, default: "
+ AndroidApiLevel.getDefault().getLevel()
+ ".",
" --intermediate # Compile an intermediate result intended for later",
@@ -243,13 +247,17 @@
builder.setMainDexListOutputPath(Paths.get(nextArg));
} else if (arg.equals("--optimize-multidex-for-linearalloc")) {
builder.setOptimizeMultidexForLinearAlloc(true);
- } else if (arg.equals("--min-api")) {
+ } else if (arg.equals(MIN_API_FLAG)) {
if (hasDefinedApiLevel) {
- builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ builder.error(
+ new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
} else {
- parseMinApi(builder, nextArg, origin);
+ parsePositiveIntArgument(builder, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
hasDefinedApiLevel = true;
}
+ } else if (arg.equals(THREAD_COUNT_FLAG)) {
+ parsePositiveIntArgument(
+ builder, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
} else if (arg.equals("--intermediate")) {
builder.setIntermediate(true);
} else if (arg.equals("--no-desugaring")) {
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index c863fe6..c32fbb7 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -86,6 +87,7 @@
DesugaredLibraryConfiguration libraryConfiguration,
List<AssertionsConfiguration> assertionsConfiguration,
List<Consumer<Inspector>> outputInspections,
+ int threadCount,
DexItemFactory factory) {
super(
inputApp,
@@ -99,7 +101,8 @@
false,
(name, checksum) -> true,
assertionsConfiguration,
- outputInspections);
+ outputInspections,
+ threadCount);
this.d8Command = d8Command;
this.r8Command = r8Command;
this.libraryConfiguration = libraryConfiguration;
@@ -184,6 +187,9 @@
new AssertionConfigurationWithDefault(
AssertionTransformation.DISABLE, getAssertionsConfiguration());
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+
return internal;
}
@@ -322,6 +328,7 @@
libraryConfiguration,
getAssertionsConfiguration(),
getOutputInspections(),
+ getThreadCount(),
factory);
}
}
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index bf35b67..976fb4a 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -19,7 +19,7 @@
public class L8CommandParser extends BaseCompilerCommandParser<L8Command, L8Command.Builder> {
private static final Set<String> OPTIONS_WITH_PARAMETER =
- ImmutableSet.of("--output", "--lib", "--min-api", "--desugared-lib");
+ ImmutableSet.of("--output", "--lib", MIN_API_FLAG, "--desugared-lib", THREAD_COUNT_FLAG);
public static void main(String[] args) throws CompilationFailedException {
L8Command command = parse(args, Origin.root()).build();
@@ -43,7 +43,10 @@
" --output <file> # Output result in <outfile>.",
" # <file> must be an existing directory or a zip file.",
" --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
+ " "
+ + MIN_API_FLAG
+ + " <number> "
+ + "# Minimum Android API level compatibility, default: "
+ AndroidApiLevel.getDefault().getLevel()
+ ".",
" --pg-conf <file> # Proguard configuration <file>.",
@@ -131,11 +134,12 @@
continue;
}
outputPath = Paths.get(nextArg);
- } else if (arg.equals("--min-api")) {
+ } else if (arg.equals(MIN_API_FLAG)) {
if (hasDefinedApiLevel) {
- builder.error(new StringDiagnostic("Cannot set multiple --min-api options", origin));
+ builder.error(
+ new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
} else {
- parseMinApi(builder, nextArg, origin);
+ parsePositiveIntArgument(builder, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
hasDefinedApiLevel = true;
}
} else if (arg.equals("--lib")) {
@@ -144,6 +148,9 @@
builder.addProguardConfigurationFiles(Paths.get(nextArg));
} else if (arg.equals("--desugared-lib")) {
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+ } else if (arg.equals(THREAD_COUNT_FLAG)) {
+ parsePositiveIntArgument(
+ builder, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
} else if (arg.startsWith("--")) {
if (!tryParseAssertionArgument(builder, arg, origin)) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index a9c92d2..611bc7a 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
import java.io.InputStream;
import java.nio.file.Path;
@@ -570,7 +571,8 @@
libraryConfiguration,
featureSplitConfiguration,
getAssertionsConfiguration(),
- getOutputInspections());
+ getOutputInspections(),
+ getThreadCount());
return command;
}
@@ -728,7 +730,8 @@
DesugaredLibraryConfiguration libraryConfiguration,
FeatureSplitConfiguration featureSplitConfiguration,
List<AssertionsConfiguration> assertionsConfiguration,
- List<Consumer<Inspector>> outputInspections) {
+ List<Consumer<Inspector>> outputInspections,
+ int threadCount) {
super(
inputApp,
mode,
@@ -741,7 +744,8 @@
encodeChecksum,
dexClassChecksumFilter,
assertionsConfiguration,
- outputInspections);
+ outputInspections,
+ threadCount);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
@@ -910,6 +914,9 @@
internal.desugaredLibraryConfiguration = libraryConfiguration;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 22ebb5e..84aa83e 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -21,13 +21,14 @@
"--output",
"--lib",
"--classpath",
- "--min-api",
+ MIN_API_FLAG,
"--main-dex-rules",
"--main-dex-list",
"--main-dex-list-output",
"--pg-conf",
"--pg-map-output",
- "--desugared-lib");
+ "--desugared-lib",
+ THREAD_COUNT_FLAG);
public static void main(String[] args) throws CompilationFailedException {
R8Command command = parse(args, Origin.root()).build();
@@ -64,7 +65,10 @@
" # <file> must be an existing directory or a zip file.",
" --lib <file|jdk-home> # Add <file|jdk-home> as a library resource.",
" --classpath <file> # Add <file> as a classpath resource.",
- " --min-api <number> # Minimum Android API level compatibility, default: "
+ " "
+ + MIN_API_FLAG
+ + " <number> "
+ + "# Minimum Android API level compatibility, default: "
+ AndroidApiLevel.getDefault().getLevel()
+ ".",
" --pg-conf <file> # Proguard configuration <file>.",
@@ -190,13 +194,18 @@
addLibraryArgument(builder, argsOrigin, nextArg);
} else if (arg.equals("--classpath")) {
builder.addClasspathFiles(Paths.get(nextArg));
- } else if (arg.equals("--min-api")) {
+ } else if (arg.equals(MIN_API_FLAG)) {
if (state.hasDefinedApiLevel) {
- builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
+ builder.error(
+ new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", argsOrigin));
} else {
- parseMinApi(builder, nextArg, argsOrigin);
+ parsePositiveIntArgument(
+ builder, MIN_API_FLAG, nextArg, argsOrigin, builder::setMinApiLevel);
state.hasDefinedApiLevel = true;
}
+ } else if (arg.equals(THREAD_COUNT_FLAG)) {
+ parsePositiveIntArgument(
+ builder, THREAD_COUNT_FLAG, nextArg, argsOrigin, builder::setThreadCount);
} else if (arg.equals("--no-tree-shaking")) {
builder.setDisableTreeShaking(true);
} else if (arg.equals("--no-minification")) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 7ff5ff4..9a6a746 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -287,7 +287,7 @@
public boolean enablePcDebugInfoOutput = false;
// Number of threads to use while processing the dex files.
- public int numberOfThreads = DETERMINISTIC_DEBUGGING ? 1 : ThreadUtils.NOT_SPECIFIED;
+ public int threadCount = DETERMINISTIC_DEBUGGING ? 1 : ThreadUtils.NOT_SPECIFIED;
// Print smali disassembly.
public boolean useSmaliSyntax = false;
// Verbose output.
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 40232ca..9bb8877 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -90,18 +90,23 @@
static ExecutorService getExecutorServiceForProcessors(int processors) {
// This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
int threads = processors <= 2 ? processors : (int) Math.ceil(Integer.min(processors, 16) / 2.0);
+ return getExecutorServiceForThreads(threads);
+ }
+
+ static ExecutorService getExecutorServiceForThreads(int threads) {
+ // Note Executors.newSingleThreadExecutor() is not used when just one thread is used. See
+ // b/67338394.
return Executors.newWorkStealingPool(threads);
}
public static ExecutorService getExecutorService(int threads) {
- // Don't use Executors.newSingleThreadExecutor() when threads == 1, see b/67338394.
return threads == NOT_SPECIFIED
? getExecutorServiceForProcessors(Runtime.getRuntime().availableProcessors())
- : Executors.newWorkStealingPool(threads);
+ : getExecutorServiceForThreads(threads);
}
public static ExecutorService getExecutorService(InternalOptions options) {
- return getExecutorService(options.numberOfThreads);
+ return getExecutorService(options.threadCount);
}
public static int getNumberOfThreads(ExecutorService service) {
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 57fc2ec..8d43d07 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.sdklib.AndroidVersion;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
@@ -24,6 +25,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -592,6 +594,32 @@
d8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
}
+ @Test
+ public void numThreadsOption() throws Exception {
+ assertEquals(ThreadUtils.NOT_SPECIFIED, parse().getThreadCount());
+ assertEquals(1, parse("--thread-count", "1").getThreadCount());
+ assertEquals(2, parse("--thread-count", "2").getThreadCount());
+ assertEquals(10, parse("--thread-count", "10").getThreadCount());
+ }
+
+ private void numThreadsOptionInvalid(String value) throws Exception {
+ final String expectedErrorContains = "Invalid argument to --thread-count";
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ expectedErrorContains, handler -> parse(handler, "--thread-count", value));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void numThreadsOptionInvalid() throws Exception {
+ numThreadsOptionInvalid("0");
+ numThreadsOptionInvalid("-1");
+ numThreadsOptionInvalid("two");
+ }
+
private D8Command parse(String... args) throws CompilationFailedException {
return D8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
index 049cdb7..b62416f 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
@@ -60,6 +61,7 @@
DiagnosticsChecker handler = new DiagnosticsChecker();
try {
runner.run(handler);
+ fail("Failure expected");
} catch (CompilationFailedException e) {
checkContains(snippet, handler.errors);
throw e;
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 060bdbb..705ecff 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
@@ -14,6 +15,7 @@
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ThreadUtils;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -257,7 +259,69 @@
AssertionTransformation.PASSTHROUGH);
}
+ @Test
+ public void numThreadsOption() throws Exception {
+ assertEquals(
+ ThreadUtils.NOT_SPECIFIED,
+ parse("--desugared-lib", ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ assertEquals(
+ 1,
+ parse(
+ "--thread-count",
+ "1",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ assertEquals(
+ 2,
+ parse(
+ "--thread-count",
+ "2",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ assertEquals(
+ 10,
+ parse(
+ "--thread-count",
+ "10",
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString())
+ .getThreadCount());
+ }
+
+ private void numThreadsOptionInvalid(String value) throws Exception {
+ final String expectedErrorContains = "Invalid argument to --thread-count";
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ expectedErrorContains,
+ handler ->
+ parse(
+ handler,
+ "--thread-count",
+ value,
+ "--desugared-lib",
+ ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString()));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void numThreadsOptionInvalid() throws Exception {
+ numThreadsOptionInvalid("0");
+ numThreadsOptionInvalid("-1");
+ numThreadsOptionInvalid("two");
+ }
+
private L8Command parse(String... args) throws CompilationFailedException {
return L8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
+
+ private L8Command parse(DiagnosticsHandler handler, String... args)
+ throws CompilationFailedException {
+ return L8Command.parse(args, EmbeddedOrigin.INSTANCE, handler).build();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 4a703d7..29a9e22 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformationScope;
@@ -20,6 +21,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -709,6 +711,32 @@
r8Command.getInternalOptions().desugaredLibraryConfiguration.getRewritePrefix().isEmpty());
}
+ @Test
+ public void numThreadsOption() throws Exception {
+ assertEquals(ThreadUtils.NOT_SPECIFIED, parse().getThreadCount());
+ assertEquals(1, parse("--thread-count", "1").getThreadCount());
+ assertEquals(2, parse("--thread-count", "2").getThreadCount());
+ assertEquals(10, parse("--thread-count", "10").getThreadCount());
+ }
+
+ private void numThreadsOptionInvalid(String value) throws Exception {
+ final String expectedErrorContains = "Invalid argument to --thread-count";
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ expectedErrorContains, handler -> parse(handler, "--thread-count", value));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void numThreadsOptionInvalid() throws Exception {
+ numThreadsOptionInvalid("0");
+ numThreadsOptionInvalid("-1");
+ numThreadsOptionInvalid("two");
+ }
+
private R8Command parse(String... args) throws CompilationFailedException {
return R8Command.parse(args, EmbeddedOrigin.INSTANCE).build();
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index a16962d..cba792d 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -41,7 +41,7 @@
// For this test just do random shuffle.
options.testing.irOrdering = NondeterministicIROrdering.getInstance();
// Only use one thread to process to process in the order decided by the callback.
- options.numberOfThreads = 1;
+ options.threadCount = 1;
// Ignore the missing classes.
options.ignoreMissingClasses = true;
// Store the generated Proguard map.
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index 16db28d..78c8172 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -167,6 +167,9 @@
help='Include timing',
default=False,
action='store_true')
+ result.add_option('--cpu-list',
+ help='Run under \'taskset\' with these CPUs. See '
+ 'the \'taskset\' -c option for the format')
return result.parse_args(argv)
@@ -575,7 +578,8 @@
stdout=stdout,
stderr=stderr,
timeout=options.timeout,
- quiet=quiet)
+ quiet=quiet,
+ cmd_prefix=['taskset', '-c', options.cpu_list] if options.cpu_list else None)
if exit_code != 0:
with open(stderr_path) as stderr:
stderr_text = stderr.read()
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 2784ea5..806205f 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -11,12 +11,13 @@
def run(tool, args, build=None, debug=True,
profile=False, track_memory_file=None, extra_args=None,
- stderr=None, stdout=None, return_stdout=False, timeout=0, quiet=False):
+ stderr=None, stdout=None, return_stdout=False, timeout=0, quiet=False,
+ cmd_prefix=[]):
if build is None:
build, args = extract_build_from_args(args)
if build:
gradle.RunGradle(['r8lib' if tool.startswith('r8lib') else 'r8'])
- cmd = []
+ cmd = cmd_prefix
if track_memory_file:
cmd.extend(['tools/track_memory.sh', track_memory_file])
cmd.append(jdk.GetJavaExecutable())