blob: 46d76d8251473ff42640c64733b37a19decad6b0 [file] [edit]
// Copyright (C) 2021 Google LLC
//
// 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.
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <ostream>
#include <random>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_set>
#include <vector>
#include "testing/base/public/benchmark.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "icing/document-builder.h"
#include "icing/file/filesystem.h"
#include "icing/proto/document.pb.h"
#include "icing/proto/persist.pb.h"
#include "icing/proto/schema.pb.h"
#include "icing/proto/term.pb.h"
#include "icing/schema-builder.h"
#include "icing/schema/schema-store.h"
#include "icing/store/document-store.h"
#include "icing/testing/common-matchers.h"
#include "icing/testing/tmp-directory.h"
#include "icing/util/clock.h"
// Run on a Linux workstation:
// $ blaze build -c opt --dynamic_mode=off --copt=-gmlt
// //icing/store:document-store_benchmark
//
// $ blaze-bin/icing/store/document-store_benchmark
// --benchmark_filter=all --benchmark_memory_usage
//
// Run on an Android device:
// $ blaze build --copt="-DGOOGLE_COMMANDLINEFLAGS_FULL_API=1"
// --config=android_arm64 -c opt --dynamic_mode=off --copt=-gmlt
// //icing/store:document-store_benchmark
//
// $ adb push blaze-bin/icing/store/document-store_benchmark
// /data/local/tmp/
//
// $ adb shell /data/local/tmp/document-store_benchmark
// --benchmark_filter=all
namespace icing {
namespace lib {
namespace {
class DestructibleDirectory {
public:
explicit DestructibleDirectory(const Filesystem& filesystem,
const std::string& dir)
: filesystem_(filesystem), dir_(dir) {
filesystem_.CreateDirectoryRecursively(dir_.c_str());
}
~DestructibleDirectory() {
filesystem_.DeleteDirectoryRecursively(dir_.c_str());
}
private:
Filesystem filesystem_;
std::string dir_;
};
DocumentProto CreateDocument(const std::string namespace_,
const std::string uri) {
return DocumentBuilder()
.SetKey(namespace_, uri)
.SetSchema("email")
.AddStringProperty("subject", "subject foo")
.AddStringProperty("body", "body bar")
.Build();
}
SchemaProto CreateSchema() {
return SchemaBuilder()
.AddType(SchemaTypeConfigBuilder()
.SetType("email")
.AddProperty(
PropertyConfigBuilder()
.SetName("subject")
.SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
.SetCardinality(CARDINALITY_OPTIONAL))
.AddProperty(
PropertyConfigBuilder()
.SetName("body")
.SetDataTypeString(TERM_MATCH_EXACT, TOKENIZER_PLAIN)
.SetCardinality(CARDINALITY_OPTIONAL)))
.Build();
}
std::unique_ptr<SchemaStore> CreateSchemaStore(Filesystem filesystem,
const std::string directory,
const Clock* clock) {
const std::string schema_store_dir = directory + "/schema";
filesystem.CreateDirectoryRecursively(schema_store_dir.data());
std::unique_ptr<SchemaStore> schema_store =
SchemaStore::Create(&filesystem, schema_store_dir, clock).ValueOrDie();
auto set_schema_status = schema_store->SetSchema(
CreateSchema(), /*ignore_errors_and_delete_documents=*/false,
/*allow_circular_schema_definitions=*/false);
if (!set_schema_status.ok()) {
ICING_LOG(ERROR) << set_schema_status.status().error_message();
}
return schema_store;
}
libtextclassifier3::StatusOr<DocumentStore::CreateResult> CreateDocumentStore(
const Filesystem* filesystem, const std::string& base_dir,
const Clock* clock, const SchemaStore* schema_store) {
return DocumentStore::Create(
filesystem, base_dir, clock, schema_store,
/*force_recovery_and_revalidate_documents=*/false,
/*namespace_id_fingerprint=*/false, /*pre_mapping_fbv=*/false,
/*use_persistent_hash_map=*/false,
PortableFileBackedProtoLog<DocumentWrapper>::kDeflateCompressionLevel,
/*initialize_stats=*/nullptr);
}
void BM_DoesDocumentExistBenchmark(benchmark::State& state) {
Filesystem filesystem;
Clock clock;
std::string directory = GetTestTempDir() + "/icing";
DestructibleDirectory ddir(filesystem, directory);
std::string document_store_dir = directory + "/store";
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, directory, &clock);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
CreateDocumentStore(&filesystem, document_store_dir, &clock,
schema_store.get()));
std::unique_ptr<DocumentStore> document_store =
std::move(create_result.document_store);
int max_document_id = 300000;
for (int i = 0; i < max_document_id; ++i) {
// Put and delete a lot of documents to fill up our derived files with
// stuff.
ICING_ASSERT_OK(document_store->Put(
CreateDocument("namespace", /*uri=*/std::to_string(i))));
ICING_ASSERT_OK(document_store->Delete("namespace",
/*uri=*/std::to_string(i),
clock.GetSystemTimeMilliseconds()));
}
std::default_random_engine random;
std::uniform_int_distribution<> dist(1, max_document_id);
for (auto s : state) {
// Check random document ids to see if they exist. Hopefully to simulate
// page faulting in different sections of our mmapped derived files.
int document_id = dist(random);
benchmark::DoNotOptimize(document_store->GetAliveDocumentFilterData(
document_id, clock.GetSystemTimeMilliseconds()));
}
}
BENCHMARK(BM_DoesDocumentExistBenchmark);
void BM_Put(benchmark::State& state) {
Filesystem filesystem;
Clock clock;
std::string directory = GetTestTempDir() + "/icing";
DestructibleDirectory ddir(filesystem, directory);
std::string document_store_dir = directory + "/store";
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, directory, &clock);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
CreateDocumentStore(&filesystem, document_store_dir, &clock,
schema_store.get()));
std::unique_ptr<DocumentStore> document_store =
std::move(create_result.document_store);
DocumentProto document = CreateDocument("namespace", "uri");
for (auto s : state) {
// It's ok that this is the same document over and over. We'll create a new
// document_id for it and still insert the proto into the underlying log.
benchmark::DoNotOptimize(document_store->Put(document));
}
}
BENCHMARK(BM_Put);
void BM_GetSameDocument(benchmark::State& state) {
Filesystem filesystem;
Clock clock;
std::string directory = GetTestTempDir() + "/icing";
DestructibleDirectory ddir(filesystem, directory);
std::string document_store_dir = directory + "/store";
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, directory, &clock);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
CreateDocumentStore(&filesystem, document_store_dir, &clock,
schema_store.get()));
std::unique_ptr<DocumentStore> document_store =
std::move(create_result.document_store);
ICING_ASSERT_OK(document_store->Put(CreateDocument("namespace", "uri")));
for (auto s : state) {
benchmark::DoNotOptimize(document_store->Get("namespace", "uri"));
}
}
BENCHMARK(BM_GetSameDocument);
void BM_Delete(benchmark::State& state) {
Filesystem filesystem;
Clock clock;
std::string directory = GetTestTempDir() + "/icing";
DestructibleDirectory ddir(filesystem, directory);
std::string document_store_dir = directory + "/store";
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, directory, &clock);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
CreateDocumentStore(&filesystem, document_store_dir, &clock,
schema_store.get()));
std::unique_ptr<DocumentStore> document_store =
std::move(create_result.document_store);
DocumentProto document = CreateDocument("namespace", "uri");
for (auto s : state) {
state.PauseTiming();
ICING_ASSERT_OK(document_store->Put(document));
state.ResumeTiming();
benchmark::DoNotOptimize(document_store->Delete(
"namespace", "uri", clock.GetSystemTimeMilliseconds()));
}
}
BENCHMARK(BM_Delete);
void BM_Create(benchmark::State& state) {
Filesystem filesystem;
Clock clock;
std::string directory = GetTestTempDir() + "/icing";
std::string document_store_dir = directory + "/store";
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, directory, &clock);
// Create an initial document store and put some data in.
{
DestructibleDirectory ddir(filesystem, directory);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
CreateDocumentStore(&filesystem, document_store_dir, &clock,
schema_store.get()));
std::unique_ptr<DocumentStore> document_store =
std::move(create_result.document_store);
DocumentProto document = CreateDocument("namespace", "uri");
ICING_ASSERT_OK(document_store->Put(document));
ICING_ASSERT_OK(document_store->PersistToDisk(PersistType::FULL));
}
// Recreating it with some content to checksum over.
DestructibleDirectory ddir(filesystem, directory);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
for (auto s : state) {
benchmark::DoNotOptimize(CreateDocumentStore(
&filesystem, document_store_dir, &clock, schema_store.get()));
}
}
BENCHMARK(BM_Create);
void BM_ComputeChecksum(benchmark::State& state) {
Filesystem filesystem;
Clock clock;
std::string directory = GetTestTempDir() + "/icing";
DestructibleDirectory ddir(filesystem, directory);
std::string document_store_dir = directory + "/store";
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, directory, &clock);
filesystem.CreateDirectoryRecursively(document_store_dir.data());
ICING_ASSERT_OK_AND_ASSIGN(
DocumentStore::CreateResult create_result,
CreateDocumentStore(&filesystem, document_store_dir, &clock,
schema_store.get()));
std::unique_ptr<DocumentStore> document_store =
std::move(create_result.document_store);
DocumentProto document = CreateDocument("namespace", "uri");
ICING_ASSERT_OK(document_store->Put(document));
ICING_ASSERT_OK(document_store->PersistToDisk(PersistType::LITE));
for (auto s : state) {
benchmark::DoNotOptimize(document_store->ComputeChecksum());
}
}
BENCHMARK(BM_ComputeChecksum);
} // namespace
} // namespace lib
} // namespace icing