summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKweku Adams <kwekua@google.com>2019-07-02 15:30:23 -0700
committerKweku Adams <kwekua@google.com>2019-07-02 15:43:06 -0700
commit8843a28151b1546c3ccf0437b6c41d2fda4c3bb3 (patch)
treeffa5e76b8f74bdb6157c1ed51b5d3d5491dabe88
parentee577653725b1a815581f5a4ba5b1fbe6e855525 (diff)
Add perf tests for JobStore file reading & writing.
1. Add perf tests to evaluate changes to JobStore's persisted file reading and writing. 2. Renaming parameters to make it clearer what they're for. 3. Removing targetSdkVersion from JobStatus because it's not being used and made testing a little harder. Current results (times are in nanoseconds): JobSchedulerPerfTests (6 Tests) ------------------------------ [1/6] com.android.frameworks.perftests.job.JobStorePerfTests#testPersistedJobReading_fewJobs_badRTC: PASSED (37.082s) testPersistedJobReading_fewJobs_badRTC_median: 16333804 testPersistedJobReading_fewJobs_badRTC_percentile95: 17523439 testPersistedJobReading_fewJobs_badRTC_stddev: 1402393 testPersistedJobReading_fewJobs_badRTC_percentile90: 17523439 testPersistedJobReading_fewJobs_badRTC_mean: 16193033 [2/6] com.android.frameworks.perftests.job.JobStorePerfTests#testPersistedJobWriting_manyJobs: PASSED (42.610s) testPersistedJobWriting_manyJobs_stddev: 47895383 testPersistedJobWriting_manyJobs_percentile95: 235706638 testPersistedJobWriting_manyJobs_median: 214356219 testPersistedJobWriting_manyJobs_percentile90: 235706638 testPersistedJobWriting_manyJobs_mean: 197006269 [3/6] com.android.frameworks.perftests.job.JobStorePerfTests#testPersistedJobReading_manyJobs_goodRTC: PASSED (40.855s) testPersistedJobReading_manyJobs_goodRTC_percentile95: 107394438 testPersistedJobReading_manyJobs_goodRTC_mean: 96397639 testPersistedJobReading_manyJobs_goodRTC_median: 95545374 testPersistedJobReading_manyJobs_goodRTC_percentile90: 107394438 testPersistedJobReading_manyJobs_goodRTC_stddev: 7879676 [4/6] com.android.frameworks.perftests.job.JobStorePerfTests#testPersistedJobWriting_fewJobs: PASSED (37.131s) testPersistedJobWriting_fewJobs_percentile90: 18209220 testPersistedJobWriting_fewJobs_stddev: 2401331 testPersistedJobWriting_fewJobs_percentile95: 18209220 testPersistedJobWriting_fewJobs_median: 13676251 testPersistedJobWriting_fewJobs_mean: 14685137 [5/6] com.android.frameworks.perftests.job.JobStorePerfTests#testPersistedJobReading_fewJobs_goodRTC: PASSED (37.170s) testPersistedJobReading_fewJobs_goodRTC_median: 13792865 testPersistedJobReading_fewJobs_goodRTC_mean: 15065688 testPersistedJobReading_fewJobs_goodRTC_percentile90: 19445106 testPersistedJobReading_fewJobs_goodRTC_percentile95: 19445106 testPersistedJobReading_fewJobs_goodRTC_stddev: 3025478 [6/6] com.android.frameworks.perftests.job.JobStorePerfTests#testPersistedJobReading_manyJobs_badRTC: PASSED (40.725s) testPersistedJobReading_manyJobs_badRTC_percentile90: 96233552 testPersistedJobReading_manyJobs_badRTC_stddev: 6633074 testPersistedJobReading_manyJobs_badRTC_mean: 89815566 testPersistedJobReading_manyJobs_badRTC_median: 91038758 testPersistedJobReading_manyJobs_badRTC_percentile95: 96233552 Bug: 135200955 Test: atest com.android.frameworks.perftests.job.JobStorePerfTests Test: atest com.android.server.job.JobStoreTest Test: atest com.android.server.job.controllers.ConnectivityControllerTest Test: atest com.android.server.job.controllers.JobStatusTest Test: atest com.android.server.job.controllers.QuotaControllerTest Change-Id: Id5cc72d6703b2b1c5d553de91b8a1837b360bc7f
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java8
-rw-r--r--services/core/java/com/android/server/job/JobStore.java24
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java23
-rw-r--r--tests/JobSchedulerPerfTests/Android.bp25
-rw-r--r--tests/JobSchedulerPerfTests/AndroidManifest.xml27
-rw-r--r--tests/JobSchedulerPerfTests/AndroidTest.xml28
-rw-r--r--tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java156
7 files changed, 265 insertions, 26 deletions
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 8181900177ef..b64342f9245d 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1494,16 +1494,16 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * Called when we want to remove a JobStatus object that we've finished executing. Returns the
- * object removed.
+ * Called when we want to remove a JobStatus object that we've finished executing.
+ * @return true if the job was removed.
*/
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
- boolean writeBack) {
+ boolean removeFromPersisted) {
// Deal with any remaining work items in the old job.
jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
// Remove from store as well as controllers.
- final boolean removed = mJobs.remove(jobStatus, writeBack);
+ final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
if (removed && mReadyToRock) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 4ef37a2e484d..d69faf37397c 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -235,10 +235,11 @@ public final class JobStore {
/**
* Remove the provided job. Will also delete the job if it was persisted.
- * @param writeBack If true, the job will be deleted (if it was persisted) immediately.
+ * @param removeFromPersisted If true, the job will be removed from the persisted job list
+ * immediately (if it was persisted).
* @return Whether or not the job existed to be removed.
*/
- public boolean remove(JobStatus jobStatus, boolean writeBack) {
+ public boolean remove(JobStatus jobStatus, boolean removeFromPersisted) {
boolean removed = mJobSet.remove(jobStatus);
if (!removed) {
if (DEBUG) {
@@ -246,7 +247,7 @@ public final class JobStore {
}
return false;
}
- if (writeBack && jobStatus.isPersisted()) {
+ if (removeFromPersisted && jobStatus.isPersisted()) {
maybeWriteStatusToDiskAsync();
}
return removed;
@@ -344,6 +345,19 @@ public final class JobStore {
new ReadJobMapFromDiskRunnable(jobSet, rtcGood).run();
}
+ /** Write persisted JobStore state to disk synchronously. Should only be used for testing. */
+ @VisibleForTesting
+ public void writeStatusToDiskForTesting() {
+ synchronized (mWriteScheduleLock) {
+ if (mWriteScheduled) {
+ throw new IllegalStateException("An asynchronous write is already scheduled.");
+ }
+
+ mWriteScheduled = mWriteInProgress = true;
+ mWriteRunnable.run();
+ }
+ }
+
/**
* Wait for any pending write to the persistent store to clear
* @param maxWaitMillis Maximum time from present to wait
@@ -1049,7 +1063,9 @@ public final class JobStore {
}
}
- static final class JobSet {
+ /** Set of all tracked jobs. */
+ @VisibleForTesting
+ public static final class JobSet {
@VisibleForTesting // Key is the getUid() originator of the jobs in each sheaf
final SparseArray<ArraySet<JobStatus>> mJobs;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index a628b17a3289..9df77cb2ea7a 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -24,7 +24,6 @@ import android.app.job.JobInfo;
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
-import android.content.pm.PackageManagerInternal;
import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
@@ -131,7 +130,6 @@ public final class JobStatus {
* that underly Sync Manager operation.
*/
final int callingUid;
- final int targetSdkVersion;
final String batteryName;
/**
@@ -344,7 +342,6 @@ public final class JobStatus {
* @param job The actual requested parameters for the job
* @param callingUid Identity of the app that is scheduling the job. This may not be the
* app in which the job is implemented; such as with sync jobs.
- * @param targetSdkVersion The targetSdkVersion of the app in which the job will run.
* @param sourcePackageName The package name of the app in which the job will run.
* @param sourceUserId The user in which the job will run
* @param standbyBucket The standby bucket that the source package is currently assigned to,
@@ -363,13 +360,12 @@ public final class JobStatus {
* @param lastFailedRunTime When did we last run this job only to have it stop incomplete?
* @param internalFlags Non-API property flags about this job
*/
- private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName,
+ private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
this.job = job;
this.callingUid = callingUid;
- this.targetSdkVersion = targetSdkVersion;
this.standbyBucket = standbyBucket;
this.baseHeartbeat = heartbeat;
@@ -439,7 +435,7 @@ public final class JobStatus {
/** Copy constructor: used specifically when cloning JobStatus objects for persistence,
* so we preserve RTC window bounds if the source object has them. */
public JobStatus(JobStatus jobStatus) {
- this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.targetSdkVersion,
+ this(jobStatus.getJob(), jobStatus.getUid(),
jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(),
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
@@ -468,7 +464,7 @@ public final class JobStatus {
long lastSuccessfulRunTime, long lastFailedRunTime,
Pair<Long, Long> persistedExecutionTimesUTC,
int innerFlags) {
- this(job, callingUid, resolveTargetSdkVersion(job), sourcePkgName, sourceUserId,
+ this(job, callingUid, sourcePkgName, sourceUserId,
standbyBucket, baseHeartbeat,
sourceTag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
@@ -491,7 +487,7 @@ public final class JobStatus {
long newEarliestRuntimeElapsedMillis,
long newLatestRuntimeElapsedMillis, int backoffAttempt,
long lastSuccessfulRunTime, long lastFailedRunTime) {
- this(rescheduling.job, rescheduling.getUid(), resolveTargetSdkVersion(rescheduling.job),
+ this(rescheduling.job, rescheduling.getUid(),
rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
rescheduling.getStandbyBucket(), newBaseHeartbeat,
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
@@ -533,7 +529,7 @@ public final class JobStatus {
long currentHeartbeat = js != null
? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
: 0;
- return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
+ return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
standbyBucket, currentHeartbeat, tag, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
@@ -681,10 +677,6 @@ public final class JobStatus {
return job.getId();
}
- public int getTargetSdkVersion() {
- return targetSdkVersion;
- }
-
public void printUniqueId(PrintWriter pw) {
UserHandle.formatUid(pw, callingUid);
pw.print("/");
@@ -1441,11 +1433,6 @@ public final class JobStatus {
}
}
- private static int resolveTargetSdkVersion(JobInfo job) {
- return LocalServices.getService(PackageManagerInternal.class)
- .getPackageTargetSdkVersion(job.getService().getPackageName());
- }
-
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) {
pw.print(prefix); UserHandle.formatUid(pw, callingUid);
diff --git a/tests/JobSchedulerPerfTests/Android.bp b/tests/JobSchedulerPerfTests/Android.bp
new file mode 100644
index 000000000000..22308076d4ff
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// 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.
+
+android_test {
+ name: "JobSchedulerPerfTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "apct-perftests-utils",
+ "services",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/JobSchedulerPerfTests/AndroidManifest.xml b/tests/JobSchedulerPerfTests/AndroidManifest.xml
new file mode 100644
index 000000000000..39e751ca2a0c
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.perftests.job">
+ <uses-sdk
+ android:minSdkVersion="21" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.perftests.job"/>
+</manifest>
diff --git a/tests/JobSchedulerPerfTests/AndroidTest.xml b/tests/JobSchedulerPerfTests/AndroidTest.xml
new file mode 100644
index 000000000000..ca4b6c83f788
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ 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.
+-->
+<configuration description="Runs JobScheduler Performance Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="JobSchedulerPerfTests.apk"/>
+ <option name="cleanup-apks" value="true"/>
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="JobSchedulerPerfTests"/>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.frameworks.perftests.job"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
diff --git a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
new file mode 100644
index 000000000000..e956be339bc4
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.android.frameworks.perftests.job;
+
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.job.JobStore;
+import com.android.server.job.JobStore.JobSet;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class JobStorePerfTests {
+ private static final String SOURCE_PACKAGE = "com.android.frameworks.perftests.job";
+ private static final int SOURCE_USER_ID = 0;
+ private static final int CALLING_UID = 10079;
+
+ private static Context sContext;
+ private static File sTestDir;
+ private static JobStore sJobStore;
+
+ private static List<JobStatus> sFewJobs = new ArrayList<>();
+ private static List<JobStatus> sManyJobs = new ArrayList<>();
+
+ @Rule
+ public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+ @BeforeClass
+ public static void setUpOnce() {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sTestDir = new File(sContext.getFilesDir(), "JobStorePerfTests");
+ sJobStore = JobStore.initAndGetForTesting(sContext, sTestDir);
+
+ for (int i = 0; i < 50; i++) {
+ sFewJobs.add(createJobStatus("fewJobs", i));
+ }
+ for (int i = 0; i < 500; i++) {
+ sManyJobs.add(createJobStatus("manyJobs", i));
+ }
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ sTestDir.deleteOnExit();
+ }
+
+ private void runPersistedJobWriting(List<JobStatus> jobList) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ sJobStore.clear();
+ for (JobStatus job : jobList) {
+ sJobStore.add(job);
+ }
+ sJobStore.waitForWriteToCompleteForTesting(10_000);
+
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ sJobStore.writeStatusToDiskForTesting();
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ }
+ }
+
+ @Test
+ public void testPersistedJobWriting_fewJobs() {
+ runPersistedJobWriting(sFewJobs);
+ }
+
+ @Test
+ public void testPersistedJobWriting_manyJobs() {
+ runPersistedJobWriting(sManyJobs);
+ }
+
+ private void runPersistedJobReading(List<JobStatus> jobList, boolean rtcIsGood) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ sJobStore.clear();
+ for (JobStatus job : jobList) {
+ sJobStore.add(job);
+ }
+ sJobStore.waitForWriteToCompleteForTesting(10_000);
+
+ JobSet jobSet = new JobSet();
+
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ sJobStore.readJobMapFromDisk(jobSet, rtcIsGood);
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ }
+ }
+
+ @Test
+ public void testPersistedJobReading_fewJobs_goodRTC() {
+ runPersistedJobReading(sFewJobs, true);
+ }
+
+ @Test
+ public void testPersistedJobReading_fewJobs_badRTC() {
+ runPersistedJobReading(sFewJobs, false);
+ }
+
+ @Test
+ public void testPersistedJobReading_manyJobs_goodRTC() {
+ runPersistedJobReading(sManyJobs, true);
+ }
+
+ @Test
+ public void testPersistedJobReading_manyJobs_badRTC() {
+ runPersistedJobReading(sManyJobs, false);
+ }
+
+ private static JobStatus createJobStatus(String testTag, int jobId) {
+ JobInfo jobInfo = new JobInfo.Builder(jobId,
+ new ComponentName(sContext, "JobStorePerfTestJobService"))
+ .setPersisted(true)
+ .build();
+ return JobStatus.createFromJobInfo(
+ jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ }
+}