benchmark:Measure virtio-blk read rate
Bug: 236123069 Test: atest MicrodroidBenchmarks Change-Id: Ia46ea8d1b4425fa280171adcce7f82a76f1a329e
This commit is contained in:
parent
b05832f408
commit
246108fdf9
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright 2022 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 com.android.microdroid.testservice;
|
||||
|
||||
/** {@hide} */
|
||||
interface IBenchmarkService {
|
||||
const int SERVICE_PORT = 5677;
|
||||
|
||||
/** Reads a file and returns the elapsed seconds for the reading. */
|
||||
double readFile(String filename, long fileSizeBytes, boolean isRand);
|
||||
}
|
|
@ -12,6 +12,7 @@ android_test {
|
|||
"MicroroidDeviceTestHelper",
|
||||
"androidx.test.runner",
|
||||
"androidx.test.ext.junit",
|
||||
"com.android.microdroid.testservice-java",
|
||||
"truth-prebuilt",
|
||||
],
|
||||
libs: ["android.system.virtualmachine"],
|
||||
|
@ -24,4 +25,12 @@ android_test {
|
|||
cc_library_shared {
|
||||
name: "MicrodroidBenchmarkNativeLib",
|
||||
srcs: ["src/native/benchmarkbinary.cpp"],
|
||||
shared_libs: [
|
||||
"android.system.virtualmachineservice-ndk",
|
||||
"com.android.microdroid.testservice-ndk",
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
"libbinder_rpc_unstable",
|
||||
"liblog",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
},
|
||||
"task": {
|
||||
"type": "microdroid_launcher",
|
||||
"command": "MicrodroidBenchmarkNativeLib.so"
|
||||
"command": "MicrodroidBenchmarkNativeLib.so",
|
||||
"args": [
|
||||
"no_io"
|
||||
]
|
||||
},
|
||||
"export_tombstones": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"os": {
|
||||
"name": "microdroid"
|
||||
},
|
||||
"task": {
|
||||
"type": "microdroid_launcher",
|
||||
"command": "MicrodroidBenchmarkNativeLib.so",
|
||||
"args": [
|
||||
"io"
|
||||
]
|
||||
},
|
||||
"apexes": [
|
||||
{
|
||||
"name": "com.android.virt"
|
||||
}
|
||||
],
|
||||
"export_tombstones": true
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.microdroid.benchmark;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
@ -22,11 +23,13 @@ import static com.google.common.truth.TruthJUnit.assume;
|
|||
|
||||
import android.app.Instrumentation;
|
||||
import android.os.Bundle;
|
||||
import android.system.virtualmachine.VirtualMachine;
|
||||
import android.system.virtualmachine.VirtualMachineConfig;
|
||||
import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
|
||||
import android.system.virtualmachine.VirtualMachineException;
|
||||
|
||||
import com.android.microdroid.test.MicrodroidDeviceTestBase;
|
||||
import com.android.microdroid.testservice.IBenchmarkService;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
|
@ -38,10 +41,13 @@ import org.junit.runners.Parameterized;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class MicrodroidBenchmarks extends MicrodroidDeviceTestBase {
|
||||
private static final String TAG = "MicrodroidBenchmarks";
|
||||
private static final int VIRTIO_BLK_TRIAL_COUNT = 5;
|
||||
|
||||
@Rule public Timeout globalTimeout = Timeout.seconds(300);
|
||||
|
||||
|
@ -159,12 +165,100 @@ public class MicrodroidBenchmarks extends MicrodroidDeviceTestBase {
|
|||
continue;
|
||||
}
|
||||
|
||||
String base = name.substring(MICRODROID_IMG_PREFIX.length(),
|
||||
name.length() - MICRODROID_IMG_SUFFIX.length());
|
||||
String metric = "avf_perf/microdroid/img_size_" + base + "_MB";
|
||||
String base =
|
||||
name.substring(
|
||||
MICRODROID_IMG_PREFIX.length(),
|
||||
name.length() - MICRODROID_IMG_SUFFIX.length());
|
||||
String metric = "avf_perf/microdroid/img_size_" + base + "_MB" + "+" + name;
|
||||
double size = Files.size(file.toPath()) / SIZE_MB;
|
||||
bundle.putDouble(metric, size);
|
||||
}
|
||||
mInstrumentation.sendStatus(0, bundle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVirtioBlkSeqReadRate() throws Exception {
|
||||
testVirtioBlkReadRate(/*isRand=*/ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVirtioBlkRandReadRate() throws Exception {
|
||||
testVirtioBlkReadRate(/*isRand=*/ true);
|
||||
}
|
||||
|
||||
private void testVirtioBlkReadRate(boolean isRand) throws Exception {
|
||||
VirtualMachineConfig.Builder builder =
|
||||
mInner.newVmConfigBuilder("assets/vm_config_io.json");
|
||||
VirtualMachineConfig config = builder.debugLevel(DebugLevel.FULL).build();
|
||||
List<Double> readRates = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < VIRTIO_BLK_TRIAL_COUNT; ++i) {
|
||||
String vmName = "test_vm_io_" + i;
|
||||
mInner.forceCreateNewVirtualMachine(vmName, config);
|
||||
VirtualMachine vm = mInner.getVirtualMachineManager().get(vmName);
|
||||
VirtioBlkVmEventListener listener = new VirtioBlkVmEventListener(readRates, isRand);
|
||||
listener.runToFinish(TAG, vm);
|
||||
}
|
||||
reportMetrics(readRates, isRand);
|
||||
}
|
||||
|
||||
private void reportMetrics(List<Double> readRates, boolean isRand) {
|
||||
double sum = 0;
|
||||
for (double rate : readRates) {
|
||||
sum += rate;
|
||||
}
|
||||
double mean = sum / readRates.size();
|
||||
double sqSum = 0;
|
||||
for (double rate : readRates) {
|
||||
sqSum += (rate - mean) * (rate - mean);
|
||||
}
|
||||
double stdDev = Math.sqrt(sqSum / (readRates.size() - 1));
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
String metricNamePrefix =
|
||||
"avf_perf/virtio-blk/"
|
||||
+ (mProtectedVm ? "protected-vm/" : "unprotected-vm/")
|
||||
+ (isRand ? "rand_read_" : "seq_read_");
|
||||
String unit = "_mb_per_sec";
|
||||
|
||||
bundle.putDouble(metricNamePrefix + "mean" + unit, mean);
|
||||
bundle.putDouble(metricNamePrefix + "std" + unit, stdDev);
|
||||
mInstrumentation.sendStatus(0, bundle);
|
||||
}
|
||||
|
||||
private static class VirtioBlkVmEventListener extends VmEventListener {
|
||||
private static final String FILENAME = APEX_ETC_FS + "microdroid_super.img";
|
||||
|
||||
private final long mFileSizeBytes;
|
||||
private final List<Double> mReadRates;
|
||||
private final boolean mIsRand;
|
||||
|
||||
VirtioBlkVmEventListener(List<Double> readRates, boolean isRand) {
|
||||
File file = new File(FILENAME);
|
||||
try {
|
||||
mFileSizeBytes = Files.size(file.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
assertThat(mFileSizeBytes).isGreaterThan((long) SIZE_MB);
|
||||
mReadRates = readRates;
|
||||
mIsRand = isRand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPayloadReady(VirtualMachine vm) {
|
||||
try {
|
||||
IBenchmarkService benchmarkService =
|
||||
IBenchmarkService.Stub.asInterface(
|
||||
vm.connectToVsockServer(IBenchmarkService.SERVICE_PORT).get());
|
||||
double elapsedSeconds =
|
||||
benchmarkService.readFile(FILENAME, mFileSizeBytes, mIsRand);
|
||||
double fileSizeMb = mFileSizeBytes / SIZE_MB;
|
||||
mReadRates.add(fileSizeMb / elapsedSeconds);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
forceStop(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,123 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <aidl/android/system/virtualmachineservice/IVirtualMachineService.h>
|
||||
#include <aidl/com/android/microdroid/testservice/BnBenchmarkService.h>
|
||||
#include <android-base/result.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/vm_sockets.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" int android_native_main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
|
||||
// do nothing for now; just leave it alive. good night.
|
||||
for (;;) {
|
||||
sleep(1000);
|
||||
#include <binder_rpc_unstable.hpp>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
|
||||
using aidl::android::system::virtualmachineservice::IVirtualMachineService;
|
||||
using android::base::ErrnoError;
|
||||
using android::base::Error;
|
||||
using android::base::Result;
|
||||
using android::base::unique_fd;
|
||||
|
||||
namespace {
|
||||
constexpr uint64_t kBlockSizeBytes = 4096;
|
||||
|
||||
class IOBenchmarkService : public aidl::com::android::microdroid::testservice::BnBenchmarkService {
|
||||
public:
|
||||
ndk::ScopedAStatus readFile(const std::string& filename, int64_t fileSizeBytes, bool isRand,
|
||||
double* out) override {
|
||||
if (auto res = read_file(filename, fileSizeBytes, isRand); res.ok()) {
|
||||
*out = res.value();
|
||||
} else {
|
||||
std::stringstream error;
|
||||
error << "Failed reading file: " << res.error();
|
||||
return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT,
|
||||
error.str().c_str());
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
/** Returns the elapsed seconds for reading the file. */
|
||||
Result<double> read_file(const std::string& filename, int64_t fileSizeBytes, bool is_rand) {
|
||||
const int64_t block_count = fileSizeBytes / kBlockSizeBytes;
|
||||
std::vector<uint64_t> offsets;
|
||||
if (is_rand) {
|
||||
std::mt19937 rd{std::random_device{}()};
|
||||
offsets.reserve(block_count);
|
||||
for (auto i = 0; i < block_count; ++i) offsets.push_back(i * kBlockSizeBytes);
|
||||
std::shuffle(offsets.begin(), offsets.end(), rd);
|
||||
}
|
||||
char buf[kBlockSizeBytes];
|
||||
|
||||
clock_t start = clock();
|
||||
unique_fd fd(open(filename.c_str(), O_RDONLY));
|
||||
if (fd.get() == -1) {
|
||||
return ErrnoError() << "Read: opening " << filename << " failed";
|
||||
}
|
||||
for (auto i = 0; i < block_count; ++i) {
|
||||
if (is_rand) {
|
||||
if (lseek(fd.get(), offsets[i], SEEK_SET) == -1) {
|
||||
return ErrnoError() << "failed to lseek";
|
||||
}
|
||||
}
|
||||
auto bytes = read(fd.get(), buf, kBlockSizeBytes);
|
||||
if (bytes == 0) {
|
||||
return Error() << "unexpected end of file";
|
||||
} else if (bytes == -1) {
|
||||
return ErrnoError() << "failed to read";
|
||||
}
|
||||
}
|
||||
return {((double)clock() - start) / CLOCKS_PER_SEC};
|
||||
}
|
||||
};
|
||||
|
||||
Result<void> run_io_benchmark_tests() {
|
||||
auto test_service = ndk::SharedRefBase::make<IOBenchmarkService>();
|
||||
auto callback = []([[maybe_unused]] void* param) {
|
||||
// Tell microdroid_manager that we're ready.
|
||||
// If we can't, abort in order to fail fast - the host won't proceed without
|
||||
// receiving the onReady signal.
|
||||
ndk::SpAIBinder binder(
|
||||
RpcClient(VMADDR_CID_HOST, IVirtualMachineService::VM_BINDER_SERVICE_PORT));
|
||||
auto vm_service = IVirtualMachineService::fromBinder(binder);
|
||||
if (vm_service == nullptr) {
|
||||
LOG(ERROR) << "failed to connect VirtualMachineService\n";
|
||||
abort();
|
||||
}
|
||||
if (auto status = vm_service->notifyPayloadReady(); !status.isOk()) {
|
||||
LOG(ERROR) << "failed to notify payload ready to virtualizationservice: "
|
||||
<< status.getDescription();
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
if (!RunRpcServerCallback(test_service->asBinder().get(), test_service->SERVICE_PORT, callback,
|
||||
nullptr)) {
|
||||
return Error() << "RPC Server failed to run";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
extern "C" int android_native_main([[maybe_unused]] int argc, char* argv[]) {
|
||||
if (strcmp(argv[1], "no_io") == 0) {
|
||||
// do nothing for now; just leave it alive. good night.
|
||||
for (;;) {
|
||||
sleep(1000);
|
||||
}
|
||||
} else if (strcmp(argv[1], "io") == 0) {
|
||||
if (auto res = run_io_benchmark_tests(); res.ok()) {
|
||||
return 0;
|
||||
} else {
|
||||
LOG(ERROR) << "IO benchmark test failed: " << res.error() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ public abstract class MicrodroidDeviceTestBase {
|
|||
protected abstract static class VmEventListener implements VirtualMachineCallback {
|
||||
private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
void runToFinish(String logTag, VirtualMachine vm)
|
||||
public void runToFinish(String logTag, VirtualMachine vm)
|
||||
throws VirtualMachineException, InterruptedException {
|
||||
vm.setCallback(mExecutorService, this);
|
||||
vm.run();
|
||||
|
@ -148,7 +148,7 @@ public abstract class MicrodroidDeviceTestBase {
|
|||
mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
void forceStop(VirtualMachine vm) {
|
||||
protected void forceStop(VirtualMachine vm) {
|
||||
try {
|
||||
vm.clearCallback();
|
||||
vm.stop();
|
||||
|
|
Loading…
Reference in New Issue