1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.perftests; 18 19 import android.graphics.Typeface; 20 import android.os.Debug; 21 import android.os.SharedMemory; 22 import android.os.SystemClock; 23 import android.perftests.utils.ManualBenchmarkState; 24 import android.perftests.utils.PerfManualStatusReporter; 25 import android.util.ArrayMap; 26 import android.util.Log; 27 28 import androidx.test.filters.LargeTest; 29 import androidx.test.runner.AndroidJUnit4; 30 31 import org.junit.Before; 32 import org.junit.Rule; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 36 import java.nio.ByteBuffer; 37 import java.nio.ByteOrder; 38 import java.util.Map; 39 40 @LargeTest 41 @RunWith(AndroidJUnit4.class) 42 public class TypefaceSerializationPerfTest { 43 44 private static final String TAG = "TypefaceSerializationPerfTest"; 45 46 // Those values were taken from android.perftests.utils.BenchmarkState. 47 // Note: we cannot use TimeUnit.toNanos() because these constants are used in annotation. 48 private static final long WARMUP_DURATION_NS = 250_000_000; // 250ms 49 private static final long TARGET_TEST_DURATION_NS = 500_000_000; // 500ms 50 51 @Rule 52 public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter(); 53 54 @Before setUp()55 public void setUp() { 56 // Parse and load the preinstalled fonts in the test process so that: 57 // (1) Updated fonts do not affect test results. 58 // (2) Lazy-loading of fonts does not affect test results (esp. testSerializeFontMap). 59 Typeface.loadPreinstalledSystemFontMap(); 60 } 61 62 // testSerializeFontMap uses the default targetTestDurationNs, which is much longer than 63 // TARGET_TEST_DURATION_NS, in order to stabilize test results. 64 @Test testSerializeFontMap()65 public void testSerializeFontMap() throws Exception { 66 Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap(); 67 ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState(); 68 69 long elapsedTime = 0; 70 while (state.keepRunning(elapsedTime)) { 71 long startTime = System.nanoTime(); 72 SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); 73 elapsedTime = System.nanoTime() - startTime; 74 sharedMemory.close(); 75 android.util.Log.i(TAG, 76 "testSerializeFontMap isWarmingUp=" + state.isWarmingUp() 77 + " elapsedTime=" + elapsedTime); 78 } 79 } 80 81 @ManualBenchmarkState.ManualBenchmarkTest( 82 warmupDurationNs = WARMUP_DURATION_NS, 83 targetTestDurationNs = TARGET_TEST_DURATION_NS) 84 @Test testSerializeFontMap_memory()85 public void testSerializeFontMap_memory() throws Exception { 86 Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap(); 87 SharedMemory memory = Typeface.serializeFontMap(systemFontMap); 88 ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState(); 89 90 while (state.keepRunning(memory.getSize())) { 91 // Rate-limiting 92 SystemClock.sleep(100); 93 } 94 } 95 96 @ManualBenchmarkState.ManualBenchmarkTest( 97 warmupDurationNs = WARMUP_DURATION_NS, 98 targetTestDurationNs = TARGET_TEST_DURATION_NS) 99 @Test testDeserializeFontMap()100 public void testDeserializeFontMap() throws Exception { 101 SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap()); 102 ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN); 103 ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState(); 104 105 ArrayMap<String, Typeface> out = new ArrayMap<>(); 106 long elapsedTime = 0; 107 while (state.keepRunning(elapsedTime)) { 108 long startTime = System.nanoTime(); 109 buffer.position(0); 110 Typeface.deserializeFontMap(buffer, out); 111 elapsedTime = System.nanoTime() - startTime; 112 for (Typeface typeface : out.values()) { 113 typeface.releaseNativeObjectForTest(); 114 } 115 out.clear(); 116 } 117 } 118 119 @ManualBenchmarkState.ManualBenchmarkTest( 120 warmupDurationNs = WARMUP_DURATION_NS, 121 targetTestDurationNs = TARGET_TEST_DURATION_NS) 122 @Test testDeserializeFontMap_memory()123 public void testDeserializeFontMap_memory() throws Exception { 124 SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap()); 125 ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN); 126 ManualBenchmarkState state = mPerfManualStatusReporter.getBenchmarkState(); 127 128 ArrayMap<String, Typeface> out = new ArrayMap<>(); 129 // Diff of native heap allocation size (in bytes) before and after deserializeFontMap. 130 // Note: we don't measure memory usage of setSystemFontMap because setSystemFontMap sets 131 // some global variables, and it's hard to clear them. 132 long heapDiff = 0; 133 // Sometimes heapDiff may become negative due to GC. 134 // Use 0 in that case to avoid crashing in keepRunning. 135 while (state.keepRunning(Math.max(0, heapDiff))) { 136 buffer.position(0); 137 long baselineSize = Debug.getNativeHeapAllocatedSize(); 138 Typeface.deserializeFontMap(buffer, out); 139 long currentSize = Debug.getNativeHeapAllocatedSize(); 140 heapDiff = currentSize - baselineSize; 141 Log.i(TAG, String.format("native heap alloc: current = %d, baseline = %d, diff = %d", 142 currentSize, baselineSize, heapDiff)); 143 // Release native objects here to minimize the impact of GC. 144 for (Typeface typeface : out.values()) { 145 typeface.releaseNativeObjectForTest(); 146 } 147 out.clear(); 148 } 149 } 150 } 151