Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8843a281 authored by Kweku Adams's avatar Kweku Adams
Browse files

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
parent ee577653
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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);
+20 −4
Original line number Diff line number Diff line
@@ -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;

+5 −18
Original line number Diff line number Diff line
@@ -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);
+25 −0
Original line number Diff line number Diff line
// 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",
}
+27 −0
Original line number Diff line number Diff line
<?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>
Loading