blob: 27657ea665ad6b7be1bf5ae0eb81ababcf831af9 [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 <lib/zx/channel.h>
#include <lib/zx/port.h>
#include <vector>
#include <fbl/string_printf.h>
#include <perftest/perftest.h>
#include "assert.h"
namespace {
// Measure the times taken to enqueue and then dequeue a message from a
// Zircon channel, on a single thread. This does not involve any
// cross-thread wakeups.
bool ChannelWriteReadTest(perftest::RepeatState* state, uint32_t message_size,
uint32_t handle_count) {
state->DeclareStep("write");
state->DeclareStep("read");
state->SetBytesProcessedPerRun(message_size);
zx::channel channel1;
zx::channel channel2;
ASSERT_OK(zx::channel::create(0, &channel1, &channel2));
std::vector<char> buffer(message_size);
std::vector<zx_handle_t> handles(handle_count);
for (auto& handle : handles) {
ASSERT_OK(zx_port_create(0, &handle));
}
while (state->KeepRunning()) {
ASSERT_OK(channel1.write(0, buffer.data(), message_size, handles.data(), handle_count));
state->NextStep();
ASSERT_OK(channel2.read(0, buffer.data(), handles.data(), message_size, handle_count, nullptr,
nullptr));
}
for (auto& handle : handles) {
ASSERT_OK(zx_handle_close(handle));
}
return true;
}
// Measure the times taken to enqueue and then dequeue a message from a
// Zircon channel, on a single thread, using the zx_channel_write_etc and
// zx_channel_read_etc system calls. This does not involve any
// cross-thread wakeups.
bool ChannelWriteEtcReadEtcTest(perftest::RepeatState* state, uint32_t message_size,
uint32_t handle_count) {
state->DeclareStep("write_etc");
state->DeclareStep("read_etc");
state->SetBytesProcessedPerRun(message_size);
zx::channel channel1;
zx::channel channel2;
ASSERT_OK(zx::channel::create(0, &channel1, &channel2));
std::vector<char> buffer(message_size);
std::vector<zx_handle_disposition_t> handle_dispositions(handle_count);
for (auto& handle_disposition : handle_dispositions) {
handle_disposition.operation = ZX_HANDLE_OP_MOVE;
handle_disposition.type = ZX_OBJ_TYPE_PORT;
handle_disposition.rights = ZX_RIGHT_SAME_RIGHTS;
handle_disposition.result = ZX_OK;
ASSERT_OK(zx_port_create(0, &handle_disposition.handle));
}
std::vector<zx_handle_info_t> handle_info(handle_count);
while (state->KeepRunning()) {
ASSERT_OK(channel1.write_etc(0, buffer.data(), message_size, handle_dispositions.data(),
handle_count));
state->NextStep();
ASSERT_OK(channel2.read_etc(0, buffer.data(), handle_info.data(), message_size, handle_count,
nullptr, nullptr));
// The original handles are invalid because they were moved. Put the handles that were read in
// the handle disposition array.
for (uint32_t i = 0; i < handle_count; i++) {
handle_dispositions[i].handle = handle_info[i].handle;
}
}
for (auto& handle_disposition : handle_dispositions) {
ASSERT_OK(zx_handle_close(handle_disposition.handle));
}
return true;
}
// Measure the times taken to enqueue and then dequeue a message from a
// Zircon channel, on a single thread, using the zx_channel_write_etc and
// zx_channel_read_etc system calls. This benchmark differs from the other
// benchmarks in this file in that it uses the ZX_CHANNEL_WRITE_USE_IOVEC
// option with zx_channel_write_etc, meaning that the input to
// zx_channel_write_etc is specified as an array of zx_channel_iovec_t rather
// than a byte array. This does not involve any cross-thread wakeups.
bool ChannelWriteEtcReadEtcIovecTest(perftest::RepeatState* state, uint32_t message_size,
uint32_t num_iovecs, uint32_t handle_count) {
state->DeclareStep("write_etc");
state->DeclareStep("read_etc");
state->SetBytesProcessedPerRun(message_size);
zx::channel channel1;
zx::channel channel2;
ASSERT_OK(zx::channel::create(0, &channel1, &channel2));
std::vector<char> buffer(message_size);
std::vector<zx_channel_iovec_t> iovecs(num_iovecs);
uint32_t bytes_per_iovec = message_size / num_iovecs;
ZX_ASSERT(bytes_per_iovec * num_iovecs == message_size);
for (uint32_t i = 0; i < num_iovecs; i++) {
iovecs[i] = zx_channel_iovec_t{
.buffer = buffer.data() + (i * bytes_per_iovec),
.capacity = bytes_per_iovec,
};
}
std::vector<zx_handle_disposition_t> handle_dispositions(handle_count);
for (auto& handle_disposition : handle_dispositions) {
handle_disposition.operation = ZX_HANDLE_OP_MOVE;
handle_disposition.type = ZX_OBJ_TYPE_PORT;
handle_disposition.rights = ZX_RIGHT_SAME_RIGHTS;
handle_disposition.result = ZX_OK;
ASSERT_OK(zx_port_create(0, &handle_disposition.handle));
}
std::vector<zx_handle_info_t> handle_infos(handle_count);
while (state->KeepRunning()) {
ASSERT_OK(channel1.write_etc(ZX_CHANNEL_WRITE_USE_IOVEC, iovecs.data(), num_iovecs,
handle_dispositions.data(), handle_count));
state->NextStep();
ASSERT_OK(channel2.read_etc(0, buffer.data(), handle_infos.data(), message_size, handle_count,
nullptr, nullptr));
}
for (auto& handle_disposition : handle_dispositions) {
ASSERT_OK(zx_handle_close(handle_disposition.handle));
}
return true;
}
void RegisterTests() {
static const unsigned kMessageSizesInBytes[] = {
64,
1 * 1024,
32 * 1024,
64 * 1024,
};
static const unsigned kHandleCounts[] = {
0,
1,
};
for (auto message_size : kMessageSizesInBytes) {
for (auto handle_count : kHandleCounts) {
auto write_read_name =
fbl::StringPrintf("Channel/WriteRead/%ubytes/%uhandles", message_size, handle_count);
perftest::RegisterTest(write_read_name.c_str(), ChannelWriteReadTest, message_size,
handle_count);
auto write_etc_read_etc_name = fbl::StringPrintf("Channel/WriteEtcReadEtc/%ubytes/%uhandles",
message_size, handle_count);
perftest::RegisterTest(write_etc_read_etc_name.c_str(), ChannelWriteEtcReadEtcTest,
message_size, handle_count);
}
}
// Fewer message sizes to use with iovec because of the combinatorial
// explosion in benchmark cases with 3 parameters.
static const unsigned kMessageSizesInBytesForIovec[] = {
64,
1 * 1024,
64 * 1024,
};
// kIovecChunkSize in message_packet.cc is 16, meaning that iovec count <= 16
// will use a fast path and store iovecs in a stack buffer.
static const unsigned kNumIovecs[] = {
1,
16,
32,
64,
};
static const unsigned kNumIovecsWithHandle[] = {
16,
64,
};
for (auto message_size : kMessageSizesInBytesForIovec) {
for (auto num_iovecs : kNumIovecs) {
const unsigned handle_count = 0;
auto write_etc_read_etc_name =
fbl::StringPrintf("Channel/WriteEtcReadEtcIovecs/%ubytes/%uiovecs_%ubytes_each/%uhandles",
message_size, num_iovecs, message_size / num_iovecs, handle_count);
perftest::RegisterTest(write_etc_read_etc_name.c_str(), ChannelWriteEtcReadEtcIovecTest,
message_size, num_iovecs, handle_count);
}
for (auto num_iovecs : kNumIovecsWithHandle) {
const unsigned handle_count = 1;
auto write_etc_read_etc_name =
fbl::StringPrintf("Channel/WriteEtcReadEtcIovecs/%ubytes/%uiovecs_%ubytes_each/%uhandles",
message_size, num_iovecs, message_size / num_iovecs, handle_count);
perftest::RegisterTest(write_etc_read_etc_name.c_str(), ChannelWriteEtcReadEtcIovecTest,
message_size, num_iovecs, handle_count);
}
}
}
PERFTEST_CTOR(RegisterTests)
} // namespace