blob: 0a982d48332bca8b96c454957c6529389f79c1b2 [file] [log] [blame]
// Copyright 2019 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/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/trace/event.h>
#include <lib/zx/time.h>
#include <iostream>
#include <memory>
#include "src/ledger/bin/app/flags.h"
#include "src/ledger/bin/fidl/include/types.h"
#include "src/ledger/bin/platform/platform.h"
#include "src/ledger/bin/testing/data_generator.h"
#include "src/ledger/bin/testing/get_ledger.h"
#include "src/ledger/bin/testing/get_page_ensure_initialized.h"
#include "src/ledger/bin/testing/page_data_generator.h"
#include "src/ledger/bin/testing/quit_on_error.h"
#include "src/ledger/bin/testing/run_with_tracing.h"
#include "src/ledger/lib/convert/convert.h"
#include "src/ledger/lib/files/scoped_tmp_dir.h"
#include "src/ledger/lib/logging/logging.h"
#include "src/ledger/lib/rng/test_random.h"
#include "src/ledger/lib/vmo/strings.h"
#include "third_party/abseil-cpp/absl/flags/flag.h"
#include "third_party/abseil-cpp/absl/flags/parse.h"
#include "third_party/abseil-cpp/absl/strings/numbers.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
ABSL_FLAG(ssize_t, entry_count, -1, "number of entries to delete");
ABSL_FLAG(ssize_t, key_size, -1, "size of the keys of entries");
ABSL_FLAG(ssize_t, value_size, -1, "size of the values of entries");
ABSL_FLAG(bool, use_inline, false,
"whether Get or GetInline method will be used "
"(the latter retrieves the entry directly as String)");
namespace ledger {
namespace {
constexpr absl::string_view kStoragePath = "/data/benchmark/ledger/get_entry";
} // namespace
// Benchmark that measures the time taken to read an entry from a page.
//
// Parameters:
// --entry-count=<int> the number of entries to be put and retrieved
// --key-size=<int> size of the keys for the entries
// --value-size=<int> the size of a single value in bytes
// --inline whether Get or GetInline method will be used (the latter retrieves
// the entry directly as String).
class GetEntryBenchmark {
public:
GetEntryBenchmark(async::Loop* loop, std::unique_ptr<sys::ComponentContext> component_context,
size_t entry_count, size_t key_size, size_t value_size, bool use_inline);
GetEntryBenchmark(const GetEntryBenchmark&) = delete;
GetEntryBenchmark& operator=(const GetEntryBenchmark&) = delete;
void Run();
private:
void Populate();
void GetSnapshot();
void GetKeys(std::unique_ptr<Token> token);
void GetNextEntry(size_t i);
void GetNextEntryInline(size_t i);
void ShutDown();
fit::closure QuitLoopClosure();
async::Loop* const loop_;
TestRandom random_;
DataGenerator generator_;
PageDataGenerator page_data_generator_;
std::unique_ptr<sys::ComponentContext> component_context_;
std::unique_ptr<Platform> platform_;
std::unique_ptr<ScopedTmpDir> tmp_dir_;
const size_t entry_count_;
const size_t key_size_;
const size_t value_size_;
const bool use_inline_;
fuchsia::sys::ComponentControllerPtr component_controller_;
LedgerPtr ledger_;
PagePtr page_;
PageSnapshotPtr snapshot_;
std::vector<std::vector<uint8_t>> keys_;
};
GetEntryBenchmark::GetEntryBenchmark(async::Loop* loop,
std::unique_ptr<sys::ComponentContext> component_context,
size_t entry_count, size_t key_size, size_t value_size,
bool use_inline)
: loop_(loop),
random_(0),
generator_(&random_),
page_data_generator_(&random_),
component_context_(std::move(component_context)),
platform_(MakePlatform()),
tmp_dir_(platform_->file_system()->CreateScopedTmpDir(
DetachedPath(convert::ToString(kStoragePath)))),
entry_count_(entry_count),
key_size_(key_size),
value_size_(value_size),
use_inline_(use_inline) {
LEDGER_DCHECK(loop_);
LEDGER_DCHECK(entry_count_ > 0);
LEDGER_DCHECK(key_size_ > 0);
LEDGER_DCHECK(value_size_ > 0);
}
void GetEntryBenchmark::Run() {
Status status = GetLedger(component_context_.get(), component_controller_.NewRequest(), nullptr,
"", "get_entry", tmp_dir_->path(), QuitLoopClosure(), &ledger_,
kDefaultGarbageCollectionPolicy);
if (QuitOnError(QuitLoopClosure(), status, "GetLedger")) {
return;
}
GetPageEnsureInitialized(&ledger_, nullptr, DelayCallback::YES, QuitLoopClosure(),
[this](Status status, PagePtr page, PageId id) {
if (QuitOnError(QuitLoopClosure(), status, "Page initialization")) {
return;
}
page_ = std::move(page);
Populate();
});
}
void GetEntryBenchmark::Populate() {
auto keys = generator_.MakeKeys(entry_count_, key_size_, entry_count_);
page_data_generator_.Populate(
&page_, std::move(keys), value_size_, entry_count_,
PageDataGenerator::ReferenceStrategy::REFERENCE, Priority::EAGER, [this](Status status) {
if (QuitOnError(QuitLoopClosure(), status, "PageGenerator::Populate")) {
return;
}
GetSnapshot();
});
}
void GetEntryBenchmark::GetSnapshot() {
TRACE_ASYNC_BEGIN("benchmark", "get_snapshot", 0);
page_->GetSnapshot(snapshot_.NewRequest(), {}, nullptr);
page_->Sync([this] {
TRACE_ASYNC_END("benchmark", "get_snapshot", 0);
TRACE_ASYNC_BEGIN("benchmark", "get_keys", 0);
GetKeys(nullptr);
});
}
void GetEntryBenchmark::GetKeys(std::unique_ptr<Token> token) {
snapshot_->GetKeys({}, std::move(token), [this](auto keys, auto next_token) {
if (!next_token) {
TRACE_ASYNC_END("benchmark", "get_keys", 0);
}
for (size_t i = 0; i < keys.size(); i++) {
keys_.push_back(std::move(keys[i]));
}
if (next_token) {
GetKeys(std::move(next_token));
return;
}
if (use_inline_) {
GetNextEntryInline(0);
} else {
GetNextEntry(0);
}
});
}
void GetEntryBenchmark::GetNextEntry(size_t i) {
if (i == entry_count_) {
ShutDown();
return;
}
TRACE_ASYNC_BEGIN("benchmark", "get_entry", i);
snapshot_->Get(std::move(keys_[i]), [this, i](fuchsia::ledger::PageSnapshot_Get_Result result) {
if (QuitOnError(QuitLoopClosure(), result, "PageShapshot::Get")) {
return;
}
TRACE_ASYNC_END("benchmark", "get_entry", i);
GetNextEntry(i + 1);
});
}
void GetEntryBenchmark::GetNextEntryInline(size_t i) {
if (i == entry_count_) {
ShutDown();
return;
}
TRACE_ASYNC_BEGIN("benchmark", "get_entry_inline", i);
snapshot_->GetInline(std::move(keys_[i]),
[this, i](fuchsia::ledger::PageSnapshot_GetInline_Result result) {
if (QuitOnError(QuitLoopClosure(), result, "PageShapshot::GetInline")) {
return;
}
TRACE_ASYNC_END("benchmark", "get_entry_inline", i);
GetNextEntryInline(i + 1);
});
}
void GetEntryBenchmark::ShutDown() {
KillLedgerProcess(&component_controller_);
loop_->Quit();
}
fit::closure GetEntryBenchmark::QuitLoopClosure() {
return [this] { loop_->Quit(); };
}
int Main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto component_context = sys::ComponentContext::Create();
ssize_t entry_count = absl::GetFlag(FLAGS_entry_count);
ssize_t key_size = absl::GetFlag(FLAGS_key_size);
ssize_t value_size = absl::GetFlag(FLAGS_value_size);
bool use_inline = absl::GetFlag(FLAGS_use_inline);
if (entry_count <= 0 || key_size <= 0 || value_size <= 0) {
std::cerr << "Incorrect parameter values" << std::endl;
return 1;
}
GetEntryBenchmark app(&loop, std::move(component_context), entry_count, key_size, value_size,
use_inline);
return RunWithTracing(&loop, [&app] { app.Run(); });
}
} // namespace ledger
int main(int argc, char** argv) { return ledger::Main(argc, argv); }