Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02f0e256 authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

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
parent d7b3be3e
Loading
Loading
Loading
Loading
+14 −13
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ android_test {
        "apct-perftests-utils",
        "ub-uiautomator",
        "collector-device-lib-platform",
        "androidx.benchmark_benchmark-macro",
    ],
    platform_apis: true,
    test_suites: ["device-tests"],
+118 −1
Original line number Diff line number Diff line
@@ -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.Test;
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 @@ public class BlobStorePerfTests {
    // 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 @@ public class BlobStorePerfTests {

    @After
    public void tearDown() {
        mContext.getFilesDir().delete();
        runShellCommand("cmd package clear " + mContext.getPackageName());
        runShellCommand("cmd blob_store idle-maintenance");
    }

@@ -109,6 +122,110 @@ public class BlobStorePerfTests {
        }
    }

    @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 @@ public class BlobStorePerfTests {

    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;
+2 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.utils.blob;

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 class FakeBlobData {

    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 @@ public class FakeBlobData {
        }
        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;
        }
    }
}
+22 −0
Original line number Diff line number Diff line
@@ -30,11 +30,14 @@ import android.util.Log;
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 @@ public class Utils {
        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());