Add read/write perf tests for BlobStore.
These tests will help us understand the difference
between local reads/writes vs blobstore reads/writes.
Bug: 226954650
Test: atest ./apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
Change-Id: Ibcc8497b4c32e90824b89e836716bdeb7490227b
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index 25c4250..9064b44 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -22,17 +22,18 @@
}
android_test {
- name: "BlobStorePerfTests",
- srcs: ["src/**/*.java"],
- static_libs: [
- "BlobStoreTestUtils",
- "androidx.test.rules",
- "androidx.annotation_annotation",
- "apct-perftests-utils",
- "ub-uiautomator",
- "collector-device-lib-platform",
- ],
- platform_apis: true,
- test_suites: ["device-tests"],
- certificate: "platform",
+ name: "BlobStorePerfTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "BlobStoreTestUtils",
+ "androidx.test.rules",
+ "androidx.annotation_annotation",
+ "apct-perftests-utils",
+ "ub-uiautomator",
+ "collector-device-lib-platform",
+ "androidx.benchmark_benchmark-macro",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ certificate: "platform",
}
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index faf61a7..665e986 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -15,15 +15,20 @@
*/
package com.android.perftests.blob;
+import static com.android.utils.blob.Utils.acquireLease;
+
import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
import android.content.Context;
+import android.os.ParcelFileDescriptor;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.PerfManualStatusReporter;
import android.perftests.utils.TraceMarkParser;
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
import android.support.test.uiautomator.UiDevice;
+import android.util.DataUnit;
+import androidx.benchmark.macro.MacrobenchmarkScope;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -36,12 +41,16 @@
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.List;
+import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -53,6 +62,8 @@
// From f/b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
private static final String ATRACE_COMPUTE_DIGEST_PREFIX = "computeBlobDigest-";
+ public static final int BUFFER_SIZE_BYTES = 16 * 1024;
+
private Context mContext;
private BlobStoreManager mBlobStoreManager;
private AtraceUtils mAtraceUtils;
@@ -85,6 +96,8 @@
@After
public void tearDown() {
+ mContext.getFilesDir().delete();
+ runShellCommand("cmd package clear " + mContext.getPackageName());
runShellCommand("cmd blob_store idle-maintenance");
}
@@ -109,6 +122,110 @@
}
}
+ @Test
+ public void testDirectReads() throws Exception {
+ final File file = new File(mContext.getDataDir(), "test_read_file");
+ final long sizeBytes = DataUnit.MEBIBYTES.toBytes(fileSizeInMb);
+ try (FileOutputStream outputStream = new FileOutputStream(file)) {
+ writeData(outputStream, sizeBytes);
+ }
+
+ long durationNs = 0;
+ while (mState.keepRunning(durationNs)) {
+ dropCache();
+ try (FileInputStream inputStream = new FileInputStream(file)) {
+ final long startTimeNs = System.nanoTime();
+ readData(inputStream, sizeBytes);
+ durationNs = System.nanoTime() - startTimeNs;
+ }
+ }
+ }
+
+ @Test
+ public void testBlobStoreReads() throws Exception {
+ final FakeBlobData blobData = prepareDataBlob(fileSizeInMb);
+ commitBlob(blobData);
+ acquireLease(mContext, blobData.getBlobHandle(), "Test Desc");
+ final long sizeBytes = DataUnit.MEBIBYTES.toBytes(fileSizeInMb);
+
+ long durationNs = 0;
+ while (mState.keepRunning(durationNs)) {
+ dropCache();
+ try (FileInputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(
+ mBlobStoreManager.openBlob(blobData.getBlobHandle()))) {
+ final long startTimeNs = System.nanoTime();
+ readData(inputStream, sizeBytes);
+ durationNs = System.nanoTime() - startTimeNs;
+ }
+ }
+
+ deleteBlob(blobData.getBlobHandle());
+ }
+
+ @Test
+ public void testDirectWrites() throws Exception {
+ final File file = new File(mContext.getDataDir(), "test_write_file");
+ final long sizeBytes = DataUnit.MEBIBYTES.toBytes(fileSizeInMb);
+
+ long durationNs = 0;
+ while (mState.keepRunning(durationNs)) {
+ file.delete();
+ dropCache();
+ try (FileOutputStream outputStream = new FileOutputStream(file)) {
+ final long startTimeNs = System.nanoTime();
+ writeData(outputStream, sizeBytes);
+ durationNs = System.nanoTime() - startTimeNs;
+ }
+ }
+ }
+
+ @Test
+ public void testBlobStoreWrites() throws Exception {
+ final FakeBlobData blobData = prepareDataBlob(fileSizeInMb);
+ final long sizeBytes = DataUnit.MEBIBYTES.toBytes(fileSizeInMb);
+
+ long durationNs = 0;
+ while (mState.keepRunning(durationNs)) {
+ final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ dropCache();
+ try (FileOutputStream outputStream = new ParcelFileDescriptor
+ .AutoCloseOutputStream(session.openWrite(0, sizeBytes))) {
+ final long startTimeNs = System.nanoTime();
+ writeData(outputStream, sizeBytes);
+ durationNs = System.nanoTime() - startTimeNs;
+ }
+ }
+ mBlobStoreManager.abandonSession(sessionId);
+ }
+ }
+
+ private void readData(FileInputStream inputStream, long sizeBytes) throws Exception {
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ long bytesRead = 0;
+ while (bytesRead < sizeBytes) {
+ bytesRead += inputStream.read(buffer);
+ }
+ }
+
+ private void writeData(FileOutputStream outputStream, long sizeBytes) throws Exception {
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ long bytesWritten = 0;
+ final Random random = new Random(0);
+ while (bytesWritten < sizeBytes) {
+ random.nextBytes(buffer);
+ final int toWrite = (bytesWritten + buffer.length <= sizeBytes)
+ ? buffer.length : (int) (sizeBytes - bytesWritten);
+ outputStream.write(buffer, 0, toWrite);
+ bytesWritten += toWrite;
+ }
+ }
+
+ private void dropCache() {
+ final MacrobenchmarkScope scope = new MacrobenchmarkScope(mContext.getPackageName(), false);
+ scope.dropKernelPageCache();
+ }
+
private void collectDigestDurationsFromTrace(TraceMarkParser parser, List<Long> durations) {
mAtraceUtils.performDump(parser, (key, slices) -> {
for (TraceMarkSlice slice : slices) {
@@ -119,7 +236,7 @@
private FakeBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
final FakeBlobData blobData = new FakeBlobData.Builder(mContext)
- .setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */)
+ .setFileSize(DataUnit.MEBIBYTES.toBytes(fileSizeInMb))
.build();
blobData.prepare();
return blobData;
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
index 56db4f9..f469298 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
@@ -17,6 +17,7 @@
import static com.android.utils.blob.Utils.BUFFER_SIZE_BYTES;
import static com.android.utils.blob.Utils.copy;
+import static com.android.utils.blob.Utils.writeRandomData;
import static com.google.common.truth.Truth.assertThat;
@@ -123,7 +124,7 @@
public void prepare() throws Exception {
try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
- writeRandomData(file, mFileSize);
+ writeRandomData(file, mRandom, mFileSize);
}
mFileDigest = FileUtils.digest(mFile, "SHA-256");
mExpiryTimeMs = System.currentTimeMillis() + mExpiryDurationMs;
@@ -239,18 +240,4 @@
}
return digest.digest();
}
-
- private void writeRandomData(RandomAccessFile file, long fileSize)
- throws Exception {
- long bytesWritten = 0;
- final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- while (bytesWritten < fileSize) {
- mRandom.nextBytes(buffer);
- final int toWrite = (bytesWritten + buffer.length <= fileSize)
- ? buffer.length : (int) (fileSize - bytesWritten);
- file.seek(bytesWritten);
- file.write(buffer, 0, toWrite);
- bytesWritten += toWrite;
- }
- }
}
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
index 2d230a7..f6c0e6d 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
@@ -30,11 +30,14 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Random;
public class Utils {
public static final String TAG = "BlobStoreTest";
@@ -164,6 +167,25 @@
runShellCmd("cmd blob_store idle-maintenance");
}
+ public static void writeRandomData(File file, long fileSizeBytes)
+ throws Exception {
+ writeRandomData(new RandomAccessFile(file, "rw"), new Random(0), fileSizeBytes);
+ }
+
+ public static void writeRandomData(RandomAccessFile file, Random random, long fileSizeBytes)
+ throws Exception {
+ long bytesWritten = 0;
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ while (bytesWritten < fileSizeBytes) {
+ random.nextBytes(buffer);
+ final int toWrite = (bytesWritten + buffer.length <= fileSizeBytes)
+ ? buffer.length : (int) (fileSizeBytes - bytesWritten);
+ file.seek(bytesWritten);
+ file.write(buffer, 0, toWrite);
+ bytesWritten += toWrite;
+ }
+ }
+
public static String runShellCmd(String cmd) throws IOException {
final UiDevice uiDevice = UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation());