blob: 4985fb4b5a2312710336d6e0bc60148d12ff53a0 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <vector>
#include <benchmark/benchmark.h>
#include <launchpad/launchpad.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include "channels.h"
constexpr int MULTI_PROCESS_WRITE_BATCH_SIZE = 10000;
class Channel : public benchmark::Fixture {
private:
void SetUp(benchmark::State& state) override {
if (zx_channel_create(0, &in, &out) != ZX_OK) {
state.SkipWithError("Failed to create channel");
}
}
void TearDown(benchmark::State& state) override {
zx_handle_close(in);
zx_handle_close(out);
}
protected:
zx_handle_t in;
zx_handle_t out;
};
BENCHMARK_F(Channel, Create)(benchmark::State& state) {
while (state.KeepRunning()) {
state.PauseTiming();
zx_handle_close(in);
zx_handle_close(out);
state.ResumeTiming();
if (zx_channel_create(0, &in, &out) != ZX_OK) {
state.SkipWithError("Failed to create channel");
return;
}
}
}
BENCHMARK_DEFINE_F(Channel, Write)(benchmark::State& state) {
std::vector<char> buffer(state.range(0));
zx_status_t status;
while (state.KeepRunning()) {
status = zx_channel_write(in, 0, buffer.data(), buffer.size(), nullptr, 0);
if (status != ZX_OK) {
state.SkipWithError("Failed to write to channel");
return;
}
// Make sure we drain the channel.
state.PauseTiming();
status = zx_channel_read(out, 0, buffer.data(), nullptr,
buffer.size(), 0, nullptr, nullptr);
if (status != ZX_OK) {
state.SkipWithError("Failed to read from channel");
return;
}
state.ResumeTiming();
}
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK_REGISTER_F(Channel, Write)
->Arg(64)
->Arg(1 * 1024)
->Arg(32 * 1024)
->Arg(64 * 1024);
BENCHMARK_DEFINE_F(Channel, Read)(benchmark::State& state) {
std::vector<char> buffer(state.range(0));
zx_status_t status;
uint64_t bytes_processed = 0;
while (state.KeepRunning()) {
// Write the data to read into the channel.
state.PauseTiming();
status = zx_channel_write(in, 0, buffer.data(), buffer.size(), nullptr, 0);
if (status != ZX_OK) {
state.SkipWithError("Failed to write to channel");
return;
}
state.ResumeTiming();
uint32_t bytes_read;
status = zx_channel_read(out, 0, buffer.data(), nullptr,
buffer.size(), 0, &bytes_read, nullptr);
if (status != ZX_OK) {
state.SkipWithError("Failed to read from channel");
return;
}
bytes_processed += bytes_read;
}
state.SetBytesProcessed(bytes_processed);
}
BENCHMARK_REGISTER_F(Channel, Read)
->Arg(64)
->Arg(1 * 1024)
->Arg(32 * 1024)
->Arg(64 * 1024);
static bool Launch(const char* arg, int range, zx_handle_t* channel,
zx_handle_t* process) {
std::string optarg = std::to_string(range);
std::vector<const char*> argv = {HELPER_PATH, arg, optarg.c_str()};
uint32_t handle_id = HELPER_HANDLE_ID;
launchpad_t* lp;
launchpad_create(0, argv[0], &lp);
launchpad_load_from_file(lp, argv[0]);
launchpad_set_args(lp, argv.size(), argv.data());
launchpad_add_handle(lp, *channel, handle_id);
const char* errmsg;
auto status = launchpad_go(lp, process, &errmsg);
*channel = ZX_HANDLE_INVALID;
return status == ZX_OK;
}
int channel_read(uint32_t num_bytes) {
zx_handle_t channel = zx_get_startup_handle(HELPER_HANDLE_ID);
if (channel == ZX_HANDLE_INVALID) {
return -1;
}
std::vector<char> buffer(num_bytes);
zx_signals_t signals;
zx_status_t status;
uint32_t bytes_read;
do {
status = zx_object_wait_one(channel,
ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
ZX_TIME_INFINITE, &signals);
if (status != ZX_OK) {
return -1;
} else if (signals & ZX_CHANNEL_PEER_CLOSED) {
return 0;
}
status = zx_channel_read(channel, 0, buffer.data(), nullptr,
buffer.size(), 0, &bytes_read, nullptr);
} while(status == ZX_OK && bytes_read == num_bytes);
return -1;
}
int channel_write(uint32_t num_bytes) {
zx_handle_t channel = zx_get_startup_handle(HELPER_HANDLE_ID);
if (channel == ZX_HANDLE_INVALID) {
return -1;
}
std::vector<char> buffer(num_bytes);
zx_signals_t signals;
zx_status_t status;
do {
for (int i = 0; i < MULTI_PROCESS_WRITE_BATCH_SIZE; i++) {
status = zx_channel_write(channel, 0, buffer.data(), buffer.size(),
nullptr, 0);
if (status != ZX_OK) {
return -1;
}
}
status = zx_object_wait_one(channel,
ZX_USER_SIGNAL_0 | ZX_CHANNEL_PEER_CLOSED,
ZX_TIME_INFINITE, &signals);
if (status != ZX_OK) {
return -1;
} else if (signals & ZX_CHANNEL_PEER_CLOSED) {
return 0;
}
status = zx_object_signal(channel, ZX_USER_SIGNAL_0, 0);
} while(status == ZX_OK);
return -1;
}
class ChannelMultiProcess : public benchmark::Fixture {
private:
void SetUp(benchmark::State& state) override {
if (zx_channel_create(0, &channel, &channel_for_process) != ZX_OK) {
state.SkipWithError("Failed to create channel");
}
}
void TearDown(benchmark::State& state) override {
zx_handle_close(channel);
if (channel_for_process != ZX_HANDLE_INVALID) {
zx_handle_close(channel_for_process);
}
if (process == ZX_HANDLE_INVALID) {
return;
}
zx_status_t status = zx_object_wait_one(process, ZX_PROCESS_TERMINATED,
ZX_TIME_INFINITE, nullptr);
if (status != ZX_OK) {
state.SkipWithError("Failed to wait for process termination");
}
}
protected:
zx_handle_t channel;
zx_handle_t channel_for_process;
zx_handle_t process = ZX_HANDLE_INVALID;
};
BENCHMARK_DEFINE_F(ChannelMultiProcess, Write)(benchmark::State& state) {
if (!Launch("--channel_read", state.range(0), &channel_for_process,
&process)) {
state.SkipWithError("Failed to launch process");
return;
}
std::vector<char> buffer(state.range(0));
zx_status_t status;
while (state.KeepRunning()) {
status = zx_channel_write(channel, 0, buffer.data(), buffer.size(), nullptr,
0);
if (status != ZX_OK) {
state.SkipWithError("Failed to write to channel");
return;
}
}
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK_REGISTER_F(ChannelMultiProcess, Write)
->Arg(64)
->Arg(1 * 1024)
->Arg(32 * 1024)
->Arg(64 * 1024);
BENCHMARK_DEFINE_F(ChannelMultiProcess, Read)(benchmark::State& state) {
if (!Launch("--channel_write", state.range(0), &channel_for_process,
&process)) {
state.SkipWithError("Failed to launch process");
return;
}
std::vector<char> buffer(state.range(0));
zx_status_t status;
uint64_t bytes_processed = 0;
while (state.KeepRunning()) {
state.PauseTiming();
int iteration = state.iterations() - 1; // state.iterations starts at 1.
if (iteration > 0 && (iteration % MULTI_PROCESS_WRITE_BATCH_SIZE == 0)) {
// Signal the process to continue writing.
zx_object_signal_peer(channel, 0, ZX_USER_SIGNAL_0);
}
status = zx_object_wait_one(channel, ZX_CHANNEL_READABLE, ZX_TIME_INFINITE,
nullptr);
if (status != ZX_OK) {
state.SkipWithError("Failed to wait for channel to be readable");
return;
}
state.ResumeTiming();
uint32_t bytes_read;
status = zx_channel_read(channel, 0, buffer.data(), nullptr,
buffer.size(), 0, &bytes_read, nullptr);
if (status != ZX_OK) {
state.SkipWithError("Failed to read from channel");
return;
}
bytes_processed += bytes_read;
}
state.SetBytesProcessed(bytes_processed);
}
BENCHMARK_REGISTER_F(ChannelMultiProcess, Read)
->Arg(64)
->Arg(1 * 1024)
->Arg(32 * 1024)
->Arg(64 * 1024);