blob: 8f5e319fd590e972dbb46e49242f6d0bc92fdf08 [file] [edit]
// Copyright (C) 2019 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 <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "icing/text_classifier/lib3/utils/base/statusor.h"
#include "testing/base/public/benchmark.h"
#include "gmock/gmock.h"
#include "third_party/absl/flags/flag.h"
#include "icing/document-builder.h"
#include "icing/file/filesystem.h"
#include "icing/index/data-indexing-handler.h"
#include "icing/index/index-processor.h"
#include "icing/index/index.h"
#include "icing/index/integer-section-indexing-handler.h"
#include "icing/index/numeric/integer-index.h"
#include "icing/index/numeric/numeric-index.h"
#include "icing/index/term-indexing-handler.h"
#include "icing/legacy/core/icing-string-util.h"
#include "icing/legacy/index/icing-filesystem.h"
#include "icing/schema/schema-store.h"
#include "icing/store/document-id.h"
#include "icing/testing/common-matchers.h"
#include "icing/testing/icu-data-file-helper.h"
#include "icing/testing/test-data.h"
#include "icing/testing/tmp-directory.h"
#include "icing/tokenization/language-segmenter-factory.h"
#include "icing/tokenization/language-segmenter.h"
#include "icing/transform/normalizer-factory.h"
#include "icing/transform/normalizer.h"
#include "icing/util/clock.h"
#include "icing/util/logging.h"
#include "icing/util/status-macros.h"
#include "icing/util/tokenized-document.h"
#include "unicode/uloc.h"
// Run on a Linux workstation:
// $ blaze build -c opt --dynamic_mode=off --copt=-gmlt
// //icing/index:index-processor_benchmark
//
// $ blaze-bin/icing/index/index-processor_benchmark
// --benchmark_filter=all
//
// Run on an Android device:
// Make target //icing/tokenization:language-segmenter depend on
// //third_party/icu
//
// Make target //icing/transform:normalizer depend on
// //third_party/icu
//
// $ blaze build --copt="-DGOOGLE_COMMANDLINEFLAGS_FULL_API=1"
// --config=android_arm64 -c opt --dynamic_mode=off --copt=-gmlt
// //icing/index:index-processor_benchmark
//
// $ adb push blaze-bin/icing/index/index-processor_benchmark
// /data/local/tmp/
//
// $ adb shell /data/local/tmp/index-processor_benchmark
// --benchmark_filter=all
// --adb
// Flag to tell the benchmark that it'll be run on an Android device via adb,
// the benchmark will set up data files accordingly.
ABSL_FLAG(bool, adb, false, "run benchmark via ADB on an Android device");
namespace icing {
namespace lib {
namespace {
using ::testing::IsTrue;
// Creates a fake type config with 10 properties (p0 - p9)
void CreateFakeTypeConfig(SchemaTypeConfigProto* type_config) {
type_config->set_schema_type("Fake_Type");
for (int i = 0; i < 10; i++) {
auto property = type_config->add_properties();
property->set_property_name(
IcingStringUtil::StringPrintf("p%d", i)); // p0 - p9
property->set_data_type(PropertyConfigProto::DataType::STRING);
property->set_cardinality(PropertyConfigProto::Cardinality::OPTIONAL);
property->mutable_string_indexing_config()->set_term_match_type(
TermMatchType::EXACT_ONLY);
property->mutable_string_indexing_config()->set_tokenizer_type(
StringIndexingConfig::TokenizerType::PLAIN);
}
}
DocumentProto CreateDocumentWithOneProperty(int content_length) {
return DocumentBuilder()
.SetKey("icing", "fake/1")
.SetSchema("Fake_Type")
.AddStringProperty("p0", std::string(content_length, 'A'))
.Build();
}
DocumentProto CreateDocumentWithTenProperties(int content_length) {
int property_length = content_length / 10;
return DocumentBuilder()
.SetKey("icing", "fake/1")
.SetSchema("Fake_Type")
.AddStringProperty("p0", std::string(property_length, 'A'))
.AddStringProperty("p1", std::string(property_length, 'B'))
.AddStringProperty("p2", std::string(property_length, 'C'))
.AddStringProperty("p3", std::string(property_length, 'D'))
.AddStringProperty("p4", std::string(property_length, 'E'))
.AddStringProperty("p5", std::string(property_length, 'F'))
.AddStringProperty("p6", std::string(property_length, 'G'))
.AddStringProperty("p7", std::string(property_length, 'H'))
.AddStringProperty("p8", std::string(property_length, 'I'))
.AddStringProperty("p9", std::string(property_length, 'J'))
.Build();
}
DocumentProto CreateDocumentWithDiacriticLetters(int content_length) {
std::string content;
while (content.length() < content_length) {
content.append("àáâãā");
}
return DocumentBuilder()
.SetKey("icing", "fake/1")
.SetSchema("Fake_Type")
.AddStringProperty("p0", content)
.Build();
}
DocumentProto CreateDocumentWithHiragana(int content_length) {
std::string content;
while (content.length() < content_length) {
content.append("あいうえお");
}
return DocumentBuilder()
.SetKey("icing", "fake/1")
.SetSchema("Fake_Type")
.AddStringProperty("p0", content)
.Build();
}
std::unique_ptr<Index> CreateIndex(const IcingFilesystem& icing_filesystem,
const Filesystem& filesystem,
const std::string& index_dir) {
Index::Options options(index_dir, /*index_merge_size=*/1024 * 1024 * 10,
/*lite_index_sort_at_indexing=*/true,
/*lite_index_sort_size=*/1024 * 8);
return Index::Create(options, &filesystem, &icing_filesystem).ValueOrDie();
}
std::unique_ptr<Normalizer> CreateNormalizer() {
return normalizer_factory::Create(
/*max_term_byte_size=*/std::numeric_limits<int>::max())
.ValueOrDie();
}
std::unique_ptr<SchemaStore> CreateSchemaStore(const Filesystem& filesystem,
const Clock* clock,
const std::string& base_dir) {
std::string schema_store_dir = base_dir + "/schema_store_test";
filesystem.CreateDirectoryRecursively(schema_store_dir.c_str());
std::unique_ptr<SchemaStore> schema_store =
SchemaStore::Create(&filesystem, schema_store_dir, clock).ValueOrDie();
SchemaProto schema;
CreateFakeTypeConfig(schema.add_types());
auto set_schema_status = schema_store->SetSchema(
schema, /*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<std::vector<std::unique_ptr<DataIndexingHandler>>>
CreateDataIndexingHandlers(const Clock* clock, const Normalizer* normalizer,
Index* index, NumericIndex<int64_t>* integer_index) {
ICING_ASSIGN_OR_RETURN(
std::unique_ptr<TermIndexingHandler> term_indexing_handler,
TermIndexingHandler::Create(
clock, normalizer, index,
/*build_property_existence_metadata_hits=*/true));
ICING_ASSIGN_OR_RETURN(
std::unique_ptr<IntegerSectionIndexingHandler>
integer_section_indexing_handler,
IntegerSectionIndexingHandler::Create(clock, integer_index));
std::vector<std::unique_ptr<DataIndexingHandler>> handlers;
handlers.push_back(std::move(term_indexing_handler));
handlers.push_back(std::move(integer_section_indexing_handler));
return handlers;
}
void CleanUp(const Filesystem& filesystem, const std::string& base_dir) {
filesystem.DeleteDirectoryRecursively(base_dir.c_str());
}
void BM_IndexDocumentWithOneProperty(benchmark::State& state) {
bool run_via_adb = absl::GetFlag(FLAGS_adb);
if (!run_via_adb) {
ICING_ASSERT_OK(icu_data_file_helper::SetUpICUDataFile(
GetTestFilePath("icing/icu.dat")));
}
IcingFilesystem icing_filesystem;
Filesystem filesystem;
std::string base_dir = GetTestTempDir() + "/index_processor_benchmark";
std::string index_dir = base_dir + "/index_test/";
std::string integer_index_dir = base_dir + "/integer_index_test/";
CleanUp(filesystem, base_dir);
ASSERT_THAT(filesystem.CreateDirectoryRecursively(base_dir.c_str()),
IsTrue());
std::unique_ptr<Index> index =
CreateIndex(icing_filesystem, filesystem, index_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<NumericIndex<int64_t>> integer_index,
IntegerIndex::Create(filesystem, integer_index_dir,
IntegerIndex::kDefaultNumDataThresholdForBucketSplit,
/*pre_mapping_fbv=*/true));
language_segmenter_factory::SegmenterOptions options(ULOC_US);
std::unique_ptr<LanguageSegmenter> language_segmenter =
language_segmenter_factory::Create(std::move(options)).ValueOrDie();
std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
Clock clock;
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, &clock, base_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<std::unique_ptr<DataIndexingHandler>> handlers,
CreateDataIndexingHandlers(&clock, normalizer.get(), index.get(),
integer_index.get()));
auto index_processor =
std::make_unique<IndexProcessor>(std::move(handlers), &clock);
DocumentProto input_document = CreateDocumentWithOneProperty(state.range(0));
TokenizedDocument tokenized_document(std::move(
TokenizedDocument::Create(schema_store.get(), language_segmenter.get(),
input_document)
.ValueOrDie()));
DocumentId document_id = 0;
for (auto _ : state) {
ICING_ASSERT_OK(
index_processor->IndexDocument(tokenized_document, document_id++));
}
index_processor.reset();
schema_store.reset();
normalizer.reset();
language_segmenter.reset();
integer_index.reset();
index.reset();
CleanUp(filesystem, base_dir);
}
BENCHMARK(BM_IndexDocumentWithOneProperty)
->Arg(1000)
->Arg(2000)
->Arg(4000)
->Arg(8000)
->Arg(16000)
->Arg(32000)
->Arg(64000)
->Arg(128000)
->Arg(256000)
->Arg(384000)
->Arg(512000)
->Arg(1024000)
->Arg(2048000)
->Arg(4096000);
void BM_IndexDocumentWithTenProperties(benchmark::State& state) {
bool run_via_adb = absl::GetFlag(FLAGS_adb);
if (!run_via_adb) {
ICING_ASSERT_OK(icu_data_file_helper::SetUpICUDataFile(
GetTestFilePath("icing/icu.dat")));
}
IcingFilesystem icing_filesystem;
Filesystem filesystem;
std::string base_dir = GetTestTempDir() + "/index_processor_benchmark";
std::string index_dir = base_dir + "/index_test/";
std::string integer_index_dir = base_dir + "/integer_index_test/";
CleanUp(filesystem, base_dir);
ASSERT_THAT(filesystem.CreateDirectoryRecursively(base_dir.c_str()),
IsTrue());
std::unique_ptr<Index> index =
CreateIndex(icing_filesystem, filesystem, index_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<NumericIndex<int64_t>> integer_index,
IntegerIndex::Create(filesystem, integer_index_dir,
IntegerIndex::kDefaultNumDataThresholdForBucketSplit,
/*pre_mapping_fbv=*/true));
language_segmenter_factory::SegmenterOptions options(ULOC_US);
std::unique_ptr<LanguageSegmenter> language_segmenter =
language_segmenter_factory::Create(std::move(options)).ValueOrDie();
std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
Clock clock;
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, &clock, base_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<std::unique_ptr<DataIndexingHandler>> handlers,
CreateDataIndexingHandlers(&clock, normalizer.get(), index.get(),
integer_index.get()));
auto index_processor =
std::make_unique<IndexProcessor>(std::move(handlers), &clock);
DocumentProto input_document =
CreateDocumentWithTenProperties(state.range(0));
TokenizedDocument tokenized_document(std::move(
TokenizedDocument::Create(schema_store.get(), language_segmenter.get(),
input_document)
.ValueOrDie()));
DocumentId document_id = 0;
for (auto _ : state) {
ICING_ASSERT_OK(
index_processor->IndexDocument(tokenized_document, document_id++));
}
index_processor.reset();
schema_store.reset();
normalizer.reset();
language_segmenter.reset();
integer_index.reset();
index.reset();
CleanUp(filesystem, base_dir);
}
BENCHMARK(BM_IndexDocumentWithTenProperties)
->Arg(1000)
->Arg(2000)
->Arg(4000)
->Arg(8000)
->Arg(16000)
->Arg(32000)
->Arg(64000)
->Arg(128000)
->Arg(256000)
->Arg(384000)
->Arg(512000)
->Arg(1024000)
->Arg(2048000)
->Arg(4096000);
void BM_IndexDocumentWithDiacriticLetters(benchmark::State& state) {
bool run_via_adb = absl::GetFlag(FLAGS_adb);
if (!run_via_adb) {
ICING_ASSERT_OK(icu_data_file_helper::SetUpICUDataFile(
GetTestFilePath("icing/icu.dat")));
}
IcingFilesystem icing_filesystem;
Filesystem filesystem;
std::string base_dir = GetTestTempDir() + "/index_processor_benchmark";
std::string index_dir = base_dir + "/index_test/";
std::string integer_index_dir = base_dir + "/integer_index_test/";
CleanUp(filesystem, base_dir);
ASSERT_THAT(filesystem.CreateDirectoryRecursively(base_dir.c_str()),
IsTrue());
std::unique_ptr<Index> index =
CreateIndex(icing_filesystem, filesystem, index_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<NumericIndex<int64_t>> integer_index,
IntegerIndex::Create(filesystem, integer_index_dir,
IntegerIndex::kDefaultNumDataThresholdForBucketSplit,
/*pre_mapping_fbv=*/true));
language_segmenter_factory::SegmenterOptions options(ULOC_US);
std::unique_ptr<LanguageSegmenter> language_segmenter =
language_segmenter_factory::Create(std::move(options)).ValueOrDie();
std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
Clock clock;
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, &clock, base_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<std::unique_ptr<DataIndexingHandler>> handlers,
CreateDataIndexingHandlers(&clock, normalizer.get(), index.get(),
integer_index.get()));
auto index_processor =
std::make_unique<IndexProcessor>(std::move(handlers), &clock);
DocumentProto input_document =
CreateDocumentWithDiacriticLetters(state.range(0));
TokenizedDocument tokenized_document(std::move(
TokenizedDocument::Create(schema_store.get(), language_segmenter.get(),
input_document)
.ValueOrDie()));
DocumentId document_id = 0;
for (auto _ : state) {
ICING_ASSERT_OK(
index_processor->IndexDocument(tokenized_document, document_id++));
}
index_processor.reset();
schema_store.reset();
normalizer.reset();
language_segmenter.reset();
integer_index.reset();
index.reset();
CleanUp(filesystem, base_dir);
}
BENCHMARK(BM_IndexDocumentWithDiacriticLetters)
->Arg(1000)
->Arg(2000)
->Arg(4000)
->Arg(8000)
->Arg(16000)
->Arg(32000)
->Arg(64000)
->Arg(128000)
->Arg(256000)
->Arg(384000)
->Arg(512000)
->Arg(1024000)
->Arg(2048000)
->Arg(4096000);
void BM_IndexDocumentWithHiragana(benchmark::State& state) {
bool run_via_adb = absl::GetFlag(FLAGS_adb);
if (!run_via_adb) {
ICING_ASSERT_OK(icu_data_file_helper::SetUpICUDataFile(
GetTestFilePath("icing/icu.dat")));
}
IcingFilesystem icing_filesystem;
Filesystem filesystem;
std::string base_dir = GetTestTempDir() + "/index_processor_benchmark";
std::string index_dir = base_dir + "/index_test/";
std::string integer_index_dir = base_dir + "/integer_index_test/";
CleanUp(filesystem, base_dir);
ASSERT_THAT(filesystem.CreateDirectoryRecursively(base_dir.c_str()),
IsTrue());
std::unique_ptr<Index> index =
CreateIndex(icing_filesystem, filesystem, index_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::unique_ptr<NumericIndex<int64_t>> integer_index,
IntegerIndex::Create(filesystem, integer_index_dir,
IntegerIndex::kDefaultNumDataThresholdForBucketSplit,
/*pre_mapping_fbv=*/true));
language_segmenter_factory::SegmenterOptions options(ULOC_US);
std::unique_ptr<LanguageSegmenter> language_segmenter =
language_segmenter_factory::Create(std::move(options)).ValueOrDie();
std::unique_ptr<Normalizer> normalizer = CreateNormalizer();
Clock clock;
std::unique_ptr<SchemaStore> schema_store =
CreateSchemaStore(filesystem, &clock, base_dir);
ICING_ASSERT_OK_AND_ASSIGN(
std::vector<std::unique_ptr<DataIndexingHandler>> handlers,
CreateDataIndexingHandlers(&clock, normalizer.get(), index.get(),
integer_index.get()));
auto index_processor =
std::make_unique<IndexProcessor>(std::move(handlers), &clock);
DocumentProto input_document = CreateDocumentWithHiragana(state.range(0));
TokenizedDocument tokenized_document(std::move(
TokenizedDocument::Create(schema_store.get(), language_segmenter.get(),
input_document)
.ValueOrDie()));
DocumentId document_id = 0;
for (auto _ : state) {
ICING_ASSERT_OK(
index_processor->IndexDocument(tokenized_document, document_id++));
}
index_processor.reset();
schema_store.reset();
normalizer.reset();
language_segmenter.reset();
integer_index.reset();
index.reset();
CleanUp(filesystem, base_dir);
}
BENCHMARK(BM_IndexDocumentWithHiragana)
->Arg(1000)
->Arg(2000)
->Arg(4000)
->Arg(8000)
->Arg(16000)
->Arg(32000)
->Arg(64000)
->Arg(128000)
->Arg(256000)
->Arg(384000)
->Arg(512000)
->Arg(1024000)
->Arg(2048000)
->Arg(4096000);
} // namespace
} // namespace lib
} // namespace icing