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

Commit ab9dd642 authored by Misha Wagner's avatar Misha Wagner Committed by Android (Google) Code Review
Browse files

Merge "Hybrid ranking of last cached processes in ProcessList lru list. Behind...

Merge "Hybrid ranking of last cached processes in ProcessList lru list. Behind flag defaulted off to enable experiments."
parents 0ba4311f 18868e29
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -8302,6 +8302,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        synchronized(this) {
            mConstants.dump(pw);
            mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
            mOomAdjuster.dumpCacheOomRankerSettings(pw);
            pw.println();
            if (dumpAll) {
                pw.println("-------------------------------------------------------------------------------");
@@ -8722,6 +8723,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                synchronized (this) {
                    mConstants.dump(pw);
                    mOomAdjuster.dumpCachedAppOptimizerSettings(pw);
                    mOomAdjuster.dumpCacheOomRankerSettings(pw);
                }
            } else if ("services".equals(cmd) || "s".equals(cmd)) {
                if (dumpClient) {
+308 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.server.am;

import android.provider.DeviceConfig;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.Executor;

/**
 * Class to re-rank a number of the least recently used processes before they
 * are assigned oom adjust scores.
 */
public class CacheOomRanker {
    @VisibleForTesting
    static final String KEY_USE_OOM_RE_RANKING = "use_oom_re_ranking";
    private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
    @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;

    private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
            new ScoreComparator();
    private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
            new CacheUseComparator();
    private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
            new LastRssComparator();
    private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
            new LastActivityTimeComparator();

    private final Object mPhenotypeFlagLock = new Object();

    @GuardedBy("mPhenotypeFlagLock")
    private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
    // Weight to apply to the LRU ordering.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
    // Weight to apply to the ordering by number of times the process has been added to the cache.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
    // Weight to apply to the ordering by RSS used by the processes.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;

    // Positions to replace in the lru list.
    @GuardedBy("mPhenotypeFlagLock")
    private int[] mLruPositions;
    // Processes to re-rank
    @GuardedBy("mPhenotypeFlagLock")
    private RankedProcessRecord[] mScoredProcessRecords;

    private final DeviceConfig.OnPropertiesChangedListener mOnFlagsChangedListener =
            new DeviceConfig.OnPropertiesChangedListener() {
                @Override
                public void onPropertiesChanged(DeviceConfig.Properties properties) {
                    synchronized (mPhenotypeFlagLock) {
                        for (String name : properties.getKeyset()) {
                            if (KEY_USE_OOM_RE_RANKING.equals(name)) {
                                updateUseOomReranking();
                            } else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
                                updateNumberToReRank();
                            } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
                                updateLruWeight();
                            } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
                                updateUsesWeight();
                            } else if (KEY_OOM_RE_RANKING_RSS_WEIGHT.equals(name)) {
                                updateRssWeight();
                            }
                        }
                    }
                }
            };

    /** Load settings from device config and register a listener for changes. */
    public void init(Executor executor) {
        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                executor, mOnFlagsChangedListener);
        synchronized (mPhenotypeFlagLock) {
            updateUseOomReranking();
            updateNumberToReRank();
            updateLruWeight();
            updateUsesWeight();
            updateRssWeight();
        }
    }

    /**
     * Returns whether oom re-ranking is enabled.
     */
    public boolean useOomReranking() {
        synchronized (mPhenotypeFlagLock) {
            return mUseOomReRanking;
        }
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateUseOomReranking() {
        mUseOomReRanking = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_USE_OOM_RE_RANKING, DEFAULT_USE_OOM_RE_RANKING);
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateNumberToReRank() {
        int previousNumberToReRank = getNumberToReRank();
        int numberToReRank = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK);
        if (previousNumberToReRank != numberToReRank) {
            mScoredProcessRecords = new RankedProcessRecord[numberToReRank];
            for (int i = 0; i < mScoredProcessRecords.length; ++i) {
                mScoredProcessRecords[i] = new RankedProcessRecord();
            }
            mLruPositions = new int[numberToReRank];
        }
    }

    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting
    int getNumberToReRank() {
        return mScoredProcessRecords == null ? 0 : mScoredProcessRecords.length;
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateLruWeight() {
        mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateUsesWeight() {
        mUsesWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_USES_WEIGHT, DEFAULT_OOM_RE_RANKING_USES_WEIGHT);
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateRssWeight() {
        mRssWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_RSS_WEIGHT, DEFAULT_OOM_RE_RANKING_RSS_WEIGHT);
    }

    /**
     * Re-rank the cached processes in the lru list with a weighted ordering
     * of lru, rss size and number of times the process has been put in the cache.
     */
    public void reRankLruCachedApps(ProcessList processList) {
        float lruWeight;
        float usesWeight;
        float rssWeight;
        int[] lruPositions;
        RankedProcessRecord[] scoredProcessRecords;

        ArrayList<ProcessRecord> lruList = processList.mLruProcesses;

        synchronized (mPhenotypeFlagLock) {
            lruWeight = mLruWeight;
            usesWeight = mUsesWeight;
            rssWeight = mRssWeight;
            lruPositions = mLruPositions;
            scoredProcessRecords = mScoredProcessRecords;
        }

        // Don't re-rank if the class hasn't been initialized with defaults.
        if (lruPositions == null || scoredProcessRecords == null) {
            return;
        }

        // Collect the least recently used processes to re-rank, only rank cached
        // processes further down the list than mLruProcessServiceStart.
        int cachedProcessPos = 0;
        for (int i = 0; i < processList.mLruProcessServiceStart
                && cachedProcessPos < scoredProcessRecords.length; ++i) {
            ProcessRecord app = lruList.get(i);
            // Processes that will be assigned a cached oom adj score.
            if (!app.killedByAm && app.thread != null && app.curAdj
                    >= ProcessList.UNKNOWN_ADJ) {
                scoredProcessRecords[cachedProcessPos].proc = app;
                scoredProcessRecords[cachedProcessPos].score = 0.0f;
                lruPositions[cachedProcessPos] = i;
                ++cachedProcessPos;
            }
        }

        // TODO maybe ensure a certain number above this in the cache before re-ranking.
        if (cachedProcessPos < scoredProcessRecords.length)  {
            // Ignore we don't have enough processes to worry about re-ranking.
            return;
        }

        // Add scores for each of the weighted features we want to rank based on.
        if (lruWeight > 0.0f) {
            // This doesn't use the LRU list ordering as after the first re-ranking
            // that will no longer be lru.
            Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
            addToScore(scoredProcessRecords, lruWeight);
        }
        if (rssWeight > 0.0f) {
            Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
            addToScore(scoredProcessRecords, rssWeight);
        }
        if (usesWeight > 0.0f) {
            Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
            addToScore(scoredProcessRecords, usesWeight);
        }

        // Re-rank by the new combined score.
        Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);

        if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
            boolean printedHeader = false;
            for (int i = 0; i < scoredProcessRecords.length; ++i) {
                if (scoredProcessRecords[i].proc.pid != lruList.get(lruPositions[i]).pid) {
                    if (!printedHeader) {
                        Slog.i(OomAdjuster.TAG, "reRankLruCachedApps");
                        printedHeader = true;
                    }
                    Slog.i(OomAdjuster.TAG, "  newPos=" + lruPositions[i] + " "
                            + scoredProcessRecords[i].proc);
                }
            }
        }

        for (int i = 0; i < scoredProcessRecords.length; ++i) {
            lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
            scoredProcessRecords[i].proc = null;
        }
    }

    private static void addToScore(RankedProcessRecord[] scores, float weight) {
        for (int i = 1; i < scores.length; ++i) {
            scores[i].score += i * weight;
        }
    }

    void dump(PrintWriter pw) {
        pw.println("CacheOomRanker settings");
        synchronized (mPhenotypeFlagLock) {
            pw.println("  " + KEY_USE_OOM_RE_RANKING + "=" + mUseOomReRanking);
            pw.println("  " + KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK + "=" + getNumberToReRank());
            pw.println("  " + KEY_OOM_RE_RANKING_LRU_WEIGHT + "=" + mLruWeight);
            pw.println("  " + KEY_OOM_RE_RANKING_USES_WEIGHT + "=" + mUsesWeight);
            pw.println("  " + KEY_OOM_RE_RANKING_RSS_WEIGHT + "=" + mRssWeight);
        }
    }

    private static class ScoreComparator implements Comparator<RankedProcessRecord> {
        @Override
        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
            return Float.compare(o1.score, o2.score);
        }
    }

    private static class LastActivityTimeComparator implements Comparator<RankedProcessRecord> {
        @Override
        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
            return Long.compare(o1.proc.lastActivityTime, o2.proc.lastActivityTime);
        }
    }

    private static class CacheUseComparator implements Comparator<RankedProcessRecord> {
        @Override
        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
            return Long.compare(o1.proc.getCacheOomRankerUseCount(),
                    o2.proc.getCacheOomRankerUseCount());
        }
    }

    private static class LastRssComparator implements Comparator<RankedProcessRecord> {
        @Override
        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
            // High RSS first to match least recently used.
            return Long.compare(o2.proc.mLastRss, o1.proc.mLastRss);
        }
    }

    private static class RankedProcessRecord {
        public ProcessRecord proc;
        public float score;
    }
}
+18 −1
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import static com.android.server.am.AppProfiler.TAG_PSS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;

import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
@@ -119,7 +120,7 @@ import java.util.Arrays;
 * All of the code required to compute proc states and oom_adj values.
 */
public final class OomAdjuster {
    private static final String TAG = "OomAdjuster";
    static final String TAG = "OomAdjuster";
    static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
    static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
    static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange";
@@ -169,6 +170,12 @@ public final class OomAdjuster {
     */
    CachedAppOptimizer mCachedAppOptimizer;

    /**
     * Re-rank apps getting a cache oom adjustment from lru to weighted order
     * based on weighted scores for LRU, PSS and cache use count.
     */
    CacheOomRanker mCacheOomRanker;

    ActivityManagerConstants mConstants;

    final long[] mTmpLong = new long[3];
@@ -331,6 +338,7 @@ public final class OomAdjuster {
        mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
        mConstants = mService.mConstants;
        mCachedAppOptimizer = new CachedAppOptimizer(mService);
        mCacheOomRanker = new CacheOomRanker();

        mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
            final int pid = msg.arg1;
@@ -361,6 +369,7 @@ public final class OomAdjuster {

    void initSettings() {
        mCachedAppOptimizer.init();
        mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());
        if (mService.mConstants.KEEP_WARMING_SERVICES.size() > 0) {
            final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
            mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
@@ -769,6 +778,9 @@ public final class OomAdjuster {
            }
        }

        if (mCacheOomRanker.useOomReranking()) {
            mCacheOomRanker.reRankLruCachedApps(mProcessList);
        }
        assignCachedAdjIfNecessary(mProcessList.mLruProcesses);

        if (computeClients) { // There won't be cycles if we didn't compute clients above.
@@ -2774,6 +2786,11 @@ public final class OomAdjuster {
        mCachedAppOptimizer.dump(pw);
    }

    @GuardedBy("mService")
    void dumpCacheOomRankerSettings(PrintWriter pw) {
        mCacheOomRanker.dump(pw);
    }

    @GuardedBy("mService")
    void updateAppFreezeStateLocked(ProcessRecord app) {
        if (!mCachedAppOptimizer.useFreezer()) {
+17 −1
Original line number Diff line number Diff line
@@ -360,6 +360,13 @@ class ProcessRecord implements WindowProcessListener {
    int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
    int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;

    // Approximates the usage count of the app, used for cache re-ranking by CacheOomRanker.
    //
    // Counts the number of times the process is re-added to the cache (i.e. setCached(false);
    // setCached(true)). This over counts, as setCached is sometimes reset while remaining in the
    // cache. However, this happens uniformly across processes, so ranking is not affected.
    private int mCacheOomRankerUseCount;

    boolean mReachable; // Whether or not this process is reachable from given process

    long mKillTime; // The timestamp in uptime when this process was killed.
@@ -828,7 +835,12 @@ class ProcessRecord implements WindowProcessListener {
    }

    void setCached(boolean cached) {
        if (mCached != cached) {
            mCached = cached;
            if (cached) {
                ++mCacheOomRankerUseCount;
            }
        }
    }

    @Override
@@ -836,6 +848,10 @@ class ProcessRecord implements WindowProcessListener {
        return mCached;
    }

    int getCacheOomRankerUseCount() {
        return mCacheOomRankerUseCount;
    }

    boolean hasActivities() {
        return mWindowProcessController.hasActivities();
    }
+2 −0
Original line number Diff line number Diff line
@@ -21,11 +21,13 @@

    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
    <uses-permission android:name="android.permission.MANAGE_APPOPS"/>
    <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/>
    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>

    <!-- needed by MasterClearReceiverTest to display a system dialog -->
    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
Loading