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

Commit be977114 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Optimize pending job queue iteration." into tm-dev

parents a98c7e84 7d154917
Loading
Loading
Loading
Loading
+52 −3
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.util.Pools;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.job.controllers.JobStatus;

import java.util.ArrayList;
@@ -66,6 +67,21 @@ class PendingJobQueue {

    private int mSize = 0;

    /**
     * Whether to batch iteration so that we pull several of an app's jobs from the queue at the
     * same time (resulting in some out of order pulls) instead of pulling purely based on the
     * sort order. Batching it this way will mean we try to run several jobs of the same app at the
     * same, resulting in fewer process restarts, and can allow the iteration runtime to amortize
     * to O(A*J) instead of O(A*J*log(A)), where A = # apps and J = average # jobs per app.
     */
    private boolean mOptimizeIteration = true;

    /**
     * Number of jobs that have been pulled from the queue in succession. Used when
     * {@link #mOptimizeIteration} is true to know when to switch to the next AppJobQueue.
     */
    private int mPullCount = 0;

    private boolean mNeedToResetIterators = false;

    void add(@NonNull JobStatus job) {
@@ -139,16 +155,44 @@ class PendingJobQueue {
                mOrderedQueues.offer(ajq);
            }
            mNeedToResetIterators = false;
            // Reset the pull count when the front of the queue changes.
            mPullCount = 0;
        } else if (mOrderedQueues.size() == 0) {
            // Something significant changed, so the priority queue was cleared. Lazily regenerate
            // the queue.
            for (int i = mCurrentQueues.size() - 1; i >= 0; --i) {
                final AppJobQueue ajq = mCurrentQueues.valueAt(i);
                mOrderedQueues.offer(ajq);
            }
            // Reset the pull count when the front of the queue changes.
            mPullCount = 0;
        }
        final int numQueues = mOrderedQueues.size();
        if (numQueues == 0) {
            return null;
        }
        final AppJobQueue earliestQueue = mOrderedQueues.poll();

        // Increase the pull limit at a slightly faster rate than log(A) increases (until A>=33).
        // The pull limit increase is intended to balance fairness (one app can't starve out others)
        // with efficiency (reducing process restarts).
        // 1-4 apps --> pullLimit = 1, 5-8 apps --> pullLimit = 2, 9+ apps --> pullLimit = 3
        final int pullLimit = mOptimizeIteration ? Math.min(3, ((numQueues - 1) >>> 2) + 1) : 1;

        final AppJobQueue earliestQueue = mOrderedQueues.peek();
        if (earliestQueue != null) {
            JobStatus job = earliestQueue.next();
            final JobStatus job = earliestQueue.next();
            // Change the front of the queue if we've pulled pullLimit jobs from the current head
            // or the current head has no more jobs to provide.
            if (++mPullCount >= pullLimit
                    || earliestQueue.peekNextTimestamp() == AppJobQueue.NO_NEXT_TIMESTAMP) {
                mOrderedQueues.poll();
                if (earliestQueue.peekNextTimestamp() != AppJobQueue.NO_NEXT_TIMESTAMP) {
                    // No need to put back in the queue if it has no more jobs to give.
                    mOrderedQueues.offer(earliestQueue);
                }
                // Reset the pull count when the front of the queue changes.
                mPullCount = 0;
            }
            return job;
        }
        return null;
@@ -186,6 +230,11 @@ class PendingJobQueue {
        mNeedToResetIterators = true;
    }

    @VisibleForTesting
    void setOptimizeIteration(boolean optimize) {
        mOptimizeIteration = optimize;
    }

    int size() {
        return mSize;
    }
+20 −4
Original line number Diff line number Diff line
@@ -211,13 +211,26 @@ public class PendingJobQueueTest {
        jobQueue.add(eC11);

        checkPendingJobInvariants(jobQueue);
        final JobStatus[] expectedOrder = new JobStatus[]{
                eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rH8, rF8, eC11, rC10, rG12, rG13, eE14};
        int idx = 0;
        JobStatus job;
        final JobStatus[] expectedPureOrder = new JobStatus[]{
                eC3, rD4, eE5, eB6, rB2, eA7, rA1, rH8, eF9, rF8, eC11, rC10, rG12, rG13, eE14};
        int idx = 0;
        jobQueue.setOptimizeIteration(false);
        jobQueue.resetIterator();
        while ((job = jobQueue.next()) != null) {
            assertEquals("List wasn't correctly sorted @ index " + idx,
                    expectedOrder[idx].getJobId(), job.getJobId());
                    expectedPureOrder[idx].getJobId(), job.getJobId());
            idx++;
        }

        final JobStatus[] expectedOptimizedOrder = new JobStatus[]{
                eC3, eC11, rD4, eE5, eE14, eB6, rB2, eA7, rA1, rH8, eF9, rF8,  rC10, rG12, rG13};
        idx = 0;
        jobQueue.setOptimizeIteration(true);
        jobQueue.resetIterator();
        while ((job = jobQueue.next()) != null) {
            assertEquals("Optimized list wasn't correctly sorted @ index " + idx,
                    expectedOptimizedOrder[idx].getJobId(), job.getJobId());
            idx++;
        }
    }
@@ -379,7 +392,9 @@ public class PendingJobQueueTest {

        JobStatus job;
        jobQueue.resetIterator();
        int count = 0;
        while ((job = jobQueue.next()) != null) {
            count++;
            final int uid = job.getSourceUid();

            // Invariant #1: All jobs are sorted by override state
@@ -436,6 +451,7 @@ public class PendingJobQueueTest {
                fail("UID " + uid + " had an EJ ordered after a regular job");
            }
        }
        assertEquals("Iterator didn't go through all jobs", jobQueue.size(), count);
    }

    private static String testJobToString(JobStatus job) {