Add Typeface memory perf test.
This test measures: - SharedMemory (mmap) byte size of serialized font map. - Native allocation (malloc) byte size of deserialized font map. Typeface is updated to avoid OOM during test. Results on oriole-userdebug: [1/5] android.graphics.perftests.TypefaceSerializationPerfTest#testSerializeFontMap: PASSED (29.222s) testSerializeFontMap_stddev (ns): 74859 testSerializeFontMap_mean (ns): 1560404 testSerializeFontMap_median (ns): 1549601 testSerializeFontMap_percentile90 (ns): 1602499 testSerializeFontMap_percentile95 (ns): 1624715 [2/5] android.graphics.perftests.TypefaceSerializationPerfTest#testSerializeFontMap_memory: PASSED (13.376s) testSerializeFontMap_memory_stddev (ns): 0 testSerializeFontMap_memory_mean (ns): 931071 testSerializeFontMap_memory_median (ns): 931071 testSerializeFontMap_memory_percentile90 (ns): 931071 testSerializeFontMap_memory_percentile95 (ns): 931071 [3/5] android.graphics.perftests.TypefaceSerializationPerfTest#testDeserializeFontMap: PASSED (32.451s) testDeserializeFontMap_stddev (ns): 60524 testDeserializeFontMap_mean (ns): 232285 testDeserializeFontMap_median (ns): 228149 testDeserializeFontMap_percentile90 (ns): 272094 testDeserializeFontMap_percentile95 (ns): 291708 [4/5] android.graphics.perftests.TypefaceSerializationPerfTest#testDeserializeFontMap_memory: PASSED (29.089s) testDeserializeFontMap_memory_stddev (ns): 134 testDeserializeFontMap_memory_mean (ns): 170209 testDeserializeFontMap_memory_median (ns): 170208 testDeserializeFontMap_memory_percentile90 (ns): 170208 testDeserializeFontMap_memory_percentile95 (ns): 170208 [5/5] android.graphics.perftests.TypefaceSerializationPerfTest#testSetSystemFontMap: PASSED (28.687s) testSetSystemFontMap_stddev (ns): 2345934 testSetSystemFontMap_mean (ns): 649662 testSetSystemFontMap_median (ns): 396260 testSetSystemFontMap_percentile90 (ns): 436483 testSetSystemFontMap_percentile95 (ns): 457479 Bug: 174672300 Fix: 238679890 Test: atest CorePerfTests:android.graphics.perftests.TypefaceSerializationPerfTest Change-Id: Idb0c2a040d3ca488134f2b91a5bcbbe33a7a59c4
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<?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"
|
||||
|
||||
@@ -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<>();
|
||||
while (state.keepRunning()) {
|
||||
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<>();
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +1444,25 @@ 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 */
|
||||
public static void loadPreinstalledSystemFontMap() {
|
||||
final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
|
||||
|
||||
Reference in New Issue
Block a user