diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml index 56fa70cfc2207..bb57161b4f6bd 100644 --- a/apct-tests/perftests/core/AndroidManifest.xml +++ b/apct-tests/perftests/core/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -33,6 +34,17 @@ android:exported="false" android:process=":some_provider" /> + + + + + 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 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 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 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; } } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index a2f5301e353fd..8e3acb8570f04 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1176,6 +1176,17 @@ public class Typeface { mWeight = nativeGetWeight(ni); } + /** + * Releases the underlying native object. + * + *

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> cache) { + for (int i = 0; i < cache.size(); i++) { + SparseArray 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();