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

Commit d7afbefd authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Android (Google) Code Review
Browse files

Merge "Add Typeface memory perf test."

parents 9f7e72b9 9feb92f9
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.android.perftests.core">

    <permission android:name="com.android.perftests.core.TestPermission" />
@@ -33,6 +34,17 @@
            android:exported="false"
            android:process=":some_provider" />

        <!-- We remove EmojiCompat initializer here because it may crash the test process
             if the initializer runs while TypefaceSerializationPerfTest is running. -->
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data android:name="androidx.emoji2.text.EmojiCompatInitializer"
                tools:node="remove" />
        </provider>

        <service
            android:name="android.view.autofill.MyAutofillService"
            android:label="PERF AutofillService"
+64 −11
Original line number Diff line number Diff line
@@ -17,10 +17,13 @@
package android.graphics.perftests;

import android.graphics.Typeface;
import android.os.Debug;
import android.os.SharedMemory;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.PerfManualStatusReporter;
import android.util.ArrayMap;
import android.util.Log;

import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,16 +40,33 @@ import java.util.Map;
@RunWith(AndroidJUnit4.class)
public class TypefaceSerializationPerfTest {

    private static final String TAG = "TypefaceSerializationPerfTest";

    @Rule
    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
    public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();

    @Test
    public void testSerializeFontMap() throws Exception {
        Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap();
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();

        while (state.keepRunning()) {
        long elapsedTime = 0;
        while (state.keepRunning(elapsedTime)) {
            long startTime = System.nanoTime();
            Typeface.serializeFontMap(systemFontMap);
            elapsedTime = System.nanoTime() - startTime;
        }
    }

    @Test
    public void testSerializeFontMap_memory() throws Exception {
        Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap();
        SharedMemory memory = Typeface.serializeFontMap(systemFontMap);
        ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();

        while (state.keepRunning(memory.getSize())) {
            // Rate-limiting
            SystemClock.sleep(100);
        }
    }

@@ -54,22 +74,54 @@ public class TypefaceSerializationPerfTest {
    public void testDeserializeFontMap() throws Exception {
        SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
        ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();

        ArrayMap<String, Typeface> out = new ArrayMap<>();
        long elapsedTime = 0;
        while (state.keepRunning(elapsedTime)) {
            long startTime = System.nanoTime();
            buffer.position(0);
            Typeface.deserializeFontMap(buffer, out);
            elapsedTime = System.nanoTime() - startTime;
        }
    }

    @Test
    public void testDeserializeFontMap_memory() throws Exception {
        SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
        ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
        ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();

        ArrayMap<String, Typeface> out = new ArrayMap<>();
        while (state.keepRunning()) {
        // Diff of native heap allocation size (in bytes) before and after deserializeFontMap.
        // Note: we don't measure memory usage of setSystemFontMap because setSystemFontMap sets
        // some global variables, and it's hard to clear them.
        long heapDiff = 0;
        // Sometimes heapDiff may become negative due to GC.
        // Use 0 in that case to avoid crashing in keepRunning.
        while (state.keepRunning(Math.max(0, heapDiff))) {
            buffer.position(0);
            long baselineSize = Debug.getNativeHeapAllocatedSize();
            Typeface.deserializeFontMap(buffer, out);
            long currentSize = Debug.getNativeHeapAllocatedSize();
            heapDiff = currentSize - baselineSize;
            Log.i(TAG, String.format("native heap alloc: current = %d, baseline = %d, diff = %d",
                    currentSize, baselineSize, heapDiff));
            // Release native objects here to minimize the impact of GC.
            for (Typeface typeface : out.values()) {
                typeface.releaseNativeObjectForTest();
            }
            out.clear();
        }
    }

    @Test
    public void testSetSystemFontMap() throws Exception {
        SharedMemory memory = null;
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState();

        while (state.keepRunning()) {
            state.pauseTiming();
        long elapsedTime = 0;
        while (state.keepRunning(elapsedTime)) {
            // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit
            // (max_map_count).
            Typeface.destroySystemFontMap();
@@ -78,8 +130,9 @@ public class TypefaceSerializationPerfTest {
                memory.close();
            }
            memory = Typeface.serializeFontMap(Typeface.getSystemFontMap());
            state.resumeTiming();
            long startTime = System.nanoTime();
            Typeface.setSystemFontMap(memory);
            elapsedTime = System.nanoTime() - startTime;
        }
    }
}
+28 −1
Original line number Diff line number Diff line
@@ -1176,6 +1176,17 @@ public class Typeface {
        mWeight = nativeGetWeight(ni);
    }

    /**
     * Releases the underlying native object.
     *
     * <p>For testing only. Do not use the instance after this method is called.
     * It is safe to call this method twice or more on the same instance.
     * @hide
     */
    public void releaseNativeObjectForTest() {
        mCleaner.run();
    }

    private static Typeface getSystemDefaultTypeface(@NonNull String familyName) {
        Typeface tf = sSystemFontMap.get(familyName);
        return tf == null ? Typeface.DEFAULT : tf;
@@ -1425,7 +1436,7 @@ public class Typeface {
    public static void destroySystemFontMap() {
        synchronized (SYSTEM_FONT_MAP_LOCK) {
            for (Typeface typeface : sSystemFontMap.values()) {
                typeface.mCleaner.run();
                typeface.releaseNativeObjectForTest();
            }
            sSystemFontMap.clear();
            if (sSystemFontMapBuffer != null) {
@@ -1433,7 +1444,23 @@ public class Typeface {
            }
            sSystemFontMapBuffer = null;
            sSystemFontMapSharedMemory = null;
            synchronized (sStyledCacheLock) {
                destroyTypefaceCacheLocked(sStyledTypefaceCache);
            }
            synchronized (sWeightCacheLock) {
                destroyTypefaceCacheLocked(sWeightTypefaceCache);
            }
        }
    }

    private static void destroyTypefaceCacheLocked(LongSparseArray<SparseArray<Typeface>> cache) {
        for (int i = 0; i < cache.size(); i++) {
            SparseArray<Typeface> array = cache.valueAt(i);
            for (int j = 0; j < array.size(); j++) {
                array.valueAt(j).releaseNativeObjectForTest();
            }
        }
        cache.clear();
    }

    /** @hide */