diff options
author | Winson Chiu <chiuwinson@google.com> | 2020-03-03 18:06:37 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-03-03 18:06:37 +0000 |
commit | f5f588e03254b21682d59a02d0f231adc39df08f (patch) | |
tree | f24485cee33e91ef63c438ef704c6849239163e2 /apct-tests | |
parent | 5083ad7e2a30f6a7bcb14a28dc11408a05937af1 (diff) | |
parent | dec01d61300d530e69a6840c7def7be3a6956a37 (diff) |
Merge changes I815bb92e,I1777f84c into rvc-dev
* changes:
Adjust AndroidPackage String interning
Add package parsing v1 vs v2 benchmark
Diffstat (limited to 'apct-tests')
-rw-r--r-- | apct-tests/perftests/core/Android.bp | 34 | ||||
-rw-r--r-- | apct-tests/perftests/core/Android.mk | 33 | ||||
-rw-r--r-- | apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt | 250 |
3 files changed, 284 insertions, 33 deletions
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp new file mode 100644 index 000000000000..984893a25605 --- /dev/null +++ b/apct-tests/perftests/core/Android.bp @@ -0,0 +1,34 @@ +android_test { + name: "CorePerfTests", + + resource_dirs: ["res"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + "src/android/os/ISomeService.aidl", + ], + + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.annotation_annotation", + "apct-perftests-overlay-apps", + "apct-perftests-resources-manager-apps", + "apct-perftests-utils", + "guava", + ], + + libs: ["android.test.base"], + + platform_apis: true, + + jni_libs: ["libperftestscore_jni"], + + // Use google-fonts/dancing-script for the performance metrics + // ANDROIDMK TRANSLATION ERROR: Only $(LOCAL_PATH)/.. values are allowed + // LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script + + test_suites: ["device-tests"], + certificate: "platform", + +} diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk deleted file mode 100644 index 968478c3f338..000000000000 --- a/apct-tests/perftests/core/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - src/android/os/ISomeService.aidl - -LOCAL_STATIC_JAVA_LIBRARIES := \ - androidx.appcompat_appcompat \ - androidx.test.rules \ - androidx.annotation_annotation \ - apct-perftests-overlay-apps \ - apct-perftests-resources-manager-apps \ - apct-perftests-utils \ - guava - -LOCAL_JAVA_LIBRARIES := android.test.base - -LOCAL_PACKAGE_NAME := CorePerfTests -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni - -# Use google-fonts/dancing-script for the performance metrics -LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script - -LOCAL_COMPATIBILITY_SUITE += device-tests -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt new file mode 100644 index 000000000000..9e463652d0b6 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/PackageParsingPerfTest.kt @@ -0,0 +1,250 @@ +/* + * 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 android.os + +import android.content.pm.PackageParser +import android.content.pm.PackageParserCacheHelper.ReadHelper +import android.content.pm.PackageParserCacheHelper.WriteHelper +import android.content.pm.parsing.ParsingPackageImpl +import android.content.pm.parsing.ParsingPackageRead +import android.content.pm.parsing.ParsingPackageUtils +import android.content.pm.parsing.result.ParseTypeImpl +import android.content.res.TypedArray +import android.perftests.utils.BenchmarkState +import android.perftests.utils.PerfStatusReporter +import androidx.test.filters.LargeTest +import com.android.internal.util.ConcurrentUtils +import libcore.io.IoUtils +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.io.File +import java.io.FileOutputStream +import java.util.concurrent.ArrayBlockingQueue +import java.util.concurrent.TimeUnit + +@LargeTest +@RunWith(Parameterized::class) +class PackageParsingPerfTest { + + companion object { + private const val PARALLEL_QUEUE_CAPACITY = 10 + private const val PARALLEL_MAX_THREADS = 4 + + private const val QUEUE_POLL_TIMEOUT_SECONDS = 5L + + // TODO: Replace this with core version of SYSTEM_PARTITIONS + val FOLDERS_TO_TEST = listOf( + Environment.getRootDirectory(), + Environment.getVendorDirectory(), + Environment.getOdmDirectory(), + Environment.getOemDirectory(), + Environment.getOemDirectory(), + Environment.getSystemExtDirectory() + ) + + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun parameters(): Array<Params> { + val apks = FOLDERS_TO_TEST + .filter(File::exists) + .map(File::walkTopDown) + .flatMap(Sequence<File>::asIterable) + .filter { it.name.endsWith(".apk") } + + return arrayOf( + Params(1, apks) { ParallelParser1(it?.let(::PackageCacher1)) }, + Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) } + ) + } + + data class Params( + val version: Int, + val apks: List<File>, + val cacheDirToParser: (File?) -> ParallelParser<*> + ) { + // For test name formatting + override fun toString() = "v$version" + } + } + + @get:Rule + var perfStatusReporter = PerfStatusReporter() + + @get:Rule + var testFolder = TemporaryFolder() + + @Parameterized.Parameter(0) + lateinit var params: Params + + private val state: BenchmarkState get() = perfStatusReporter.benchmarkState + private val apks: List<File> get() = params.apks + + @Test + fun sequentialNoCache() { + params.cacheDirToParser(null).use { parser -> + while (state.keepRunning()) { + apks.forEach { parser.parse(it) } + } + } + } + + @Test + fun sequentialCached() { + params.cacheDirToParser(testFolder.newFolder()).use { parser -> + // Fill the cache + apks.forEach { parser.parse(it) } + + while (state.keepRunning()) { + apks.forEach { parser.parse(it) } + } + } + } + + @Test + fun parallelNoCache() { + params.cacheDirToParser(null).use { parser -> + while (state.keepRunning()) { + apks.forEach { parser.submit(it) } + repeat(apks.size) { parser.take() } + } + } + } + + @Test + fun parallelCached() { + params.cacheDirToParser(testFolder.newFolder()).use { parser -> + // Fill the cache + apks.forEach { parser.parse(it) } + + while (state.keepRunning()) { + apks.forEach { parser.submit(it) } + repeat(apks.size) { parser.take() } + } + } + } + + abstract class ParallelParser<PackageType : Parcelable>( + private val cacher: PackageCacher<PackageType>? = null + ) : AutoCloseable { + private val queue = ArrayBlockingQueue<Any>(PARALLEL_QUEUE_CAPACITY) + private val service = ConcurrentUtils.newFixedThreadPool( + PARALLEL_MAX_THREADS, "package-parsing-test", + Process.THREAD_PRIORITY_FOREGROUND) + + fun submit(file: File) = service.submit { queue.put(parse(file)) } + + fun take() = queue.poll(QUEUE_POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS) + + override fun close() { + service.shutdownNow() + } + + fun parse(file: File) = cacher?.getCachedResult(file) + ?: parseImpl(file).also { cacher?.cacheResult(file, it) } + + protected abstract fun parseImpl(file: File): PackageType + } + + class ParallelParser1(private val cacher: PackageCacher1? = null) + : ParallelParser<PackageParser.Package>(cacher) { + val parser = PackageParser().apply { + setCallback { true } + } + + override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null) + } + + class ParallelParser2(cacher: PackageCacher2? = null) + : ParallelParser<ParsingPackageRead>(cacher) { + val input = ThreadLocal.withInitial { ParseTypeImpl() } + val parser = ParsingPackageUtils(false, null, null, + object : ParsingPackageUtils.Callback { + override fun hasFeature(feature: String) = true + + override fun startParsingPackage( + packageName: String, + baseCodePath: String, + codePath: String, + manifestArray: TypedArray, + isCoreApp: Boolean + ) = ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray) + }) + + override fun parseImpl(file: File) = + parser.parsePackage(input.get()!!.reset(), file, 0).result + } + + abstract class PackageCacher<PackageType : Parcelable>(private val cacheDir: File) { + + fun getCachedResult(file: File): PackageType? { + val cacheFile = File(cacheDir, file.name) + if (!cacheFile.exists()) { + return null + } + + val bytes = IoUtils.readFileAsByteArray(cacheFile.absolutePath) + val parcel = Parcel.obtain().apply { + unmarshall(bytes, 0, bytes.size) + setDataPosition(0) + } + ReadHelper(parcel).apply { startAndInstall() } + return fromParcel(parcel).also { + parcel.recycle() + } + } + + fun cacheResult(file: File, parsed: Parcelable) { + val cacheFile = File(cacheDir, file.name) + if (cacheFile.exists()) { + if (!cacheFile.delete()) { + throw IllegalStateException("Unable to delete cache file: $cacheFile") + } + } + val cacheEntry = toCacheEntry(parsed) + return FileOutputStream(cacheFile).use { fos -> fos.write(cacheEntry) } + } + + private fun toCacheEntry(pkg: Parcelable): ByteArray { + val parcel = Parcel.obtain() + val helper = WriteHelper(parcel) + pkg.writeToParcel(parcel, 0 /* flags */) + helper.finishAndUninstall() + return parcel.marshall().also { + parcel.recycle() + } + } + + protected abstract fun fromParcel(parcel: Parcel): PackageType + } + + /** + * Re-implementation of v1's cache, since that's gone in R+. + */ + class PackageCacher1(cacheDir: File) : PackageCacher<PackageParser.Package>(cacheDir) { + override fun fromParcel(parcel: Parcel) = PackageParser.Package(parcel) + } + + /** + * Re-implementation of the server side PackageCacher, as it's inaccessible here. + */ + class PackageCacher2(cacheDir: File) : PackageCacher<ParsingPackageRead>(cacheDir) { + override fun fromParcel(parcel: Parcel) = ParsingPackageImpl(parcel) + } +} |