blob: ef4a5f0c9636d7b6fcbae1b449d2a10db8877758 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/persistent_cache/persistent_cache.h"
#include <algorithm>
#include "base/auto_reset.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/functional/function_ref.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gmock_expected_support.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "build/buildflag.h"
#include "components/persistent_cache/backend.h"
#include "components/persistent_cache/entry.h"
#include "components/persistent_cache/sqlite/test_helper.h"
#include "components/persistent_cache/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"
namespace persistent_cache {
class PersistentCachePerftest : public testing::Test {
protected:
std::unique_ptr<PersistentCache> CreateCache() {
std::unique_ptr<Backend> backend =
provider_.CreateBackendWithFiles(BackendType::kSqlite);
CHECK(backend);
return std::make_unique<PersistentCache>(std::move(backend));
}
void RunAndTimeTest(std::string operation_name,
int iteration_count,
base::FunctionRef<void()> test_body) {
base::AutoReset<bool> resetter(&under_measurment_, true);
base::ElapsedTimer elapsed_timer;
base::ElapsedThreadTimer elapsed_thread_timer;
test_body();
ReportMeasurment(operation_name, iteration_count, elapsed_timer.Elapsed(),
elapsed_thread_timer.Elapsed());
}
// Pregenerates keys. Use to avoid timing allocation overhead.
std::vector<std::string> GenerateKeys(int iteration_count) {
CHECK(!under_measurment_);
std::vector<std::string> keys(iteration_count);
std::generate(keys.begin(), keys.end(),
[i = 0]() mutable { return base::NumberToString(i++); });
return keys;
}
// Generates a value buffer to be inserted according to params. Should be done
// outside of timing to avoid measuring overhead.
base::HeapArray<uint8_t> MakeValue() {
CHECK(!under_measurment_);
// Median size of entries for a use case of PersistentCache as reported by
// UMA on November 7th 2025.
static constexpr size_t kValueSize = 6958;
auto value = base::HeapArray<uint8_t>::Uninit(kValueSize);
// Fill the data with random bytes to avoid unknown optimizations for
// identical pages.
base::RandBytes(value);
return value;
}
private:
void ReportMeasurment(std::string operation_name,
int iteration_count,
base::TimeDelta elapsed_time,
base::TimeDelta elapsed_thread_time) {
const std::string reporter_name("PersistentCache");
perf_test::PerfResultReporter reporter(reporter_name, operation_name);
reporter.RegisterImportantMetric(".wall_time", "us");
reporter.AddResult(
".wall_time",
static_cast<size_t>(elapsed_time.InMicroseconds() / iteration_count));
reporter.RegisterImportantMetric(".thread_time", "us");
reporter.AddResult(
".thread_time",
static_cast<size_t>(elapsed_thread_time.InMicroseconds() /
iteration_count));
}
test_support::TestHelper provider_;
bool under_measurment_ = false;
};
#if !BUILDFLAG(IS_FUCHSIA)
TEST_F(PersistentCachePerftest, OpenClose) {
auto persistent_cache = CreateCache();
static constexpr int kIterationCount = 1024;
int success_count = 0;
RunAndTimeTest("OpenClose", kIterationCount, [&] {
for (size_t i = 0; i < kIterationCount; ++i) {
auto persistent_cache_under_test = PersistentCache::Open(
*persistent_cache->ExportReadWriteBackendParams());
if (persistent_cache_under_test) {
++success_count;
}
}
});
ASSERT_EQ(success_count, kIterationCount);
}
TEST_F(PersistentCachePerftest, Insert) {
auto persistent_cache = CreateCache();
static constexpr int kIterationCount = 1024;
std::vector<std::string> keys = GenerateKeys(kIterationCount);
base::HeapArray<uint8_t> value = MakeValue();
int success_count = 0;
RunAndTimeTest("Insert", kIterationCount, [&] {
success_count = std::ranges::count_if(
keys, [&cache = *persistent_cache, &value](const auto& key) {
return cache.Insert(key, value.as_span()).has_value();
});
});
ASSERT_EQ(success_count, kIterationCount);
}
TEST_F(PersistentCachePerftest, Find) {
auto persistent_cache = CreateCache();
static constexpr int kIterationCount = 1024;
std::vector<std::string> keys = GenerateKeys(kIterationCount);
base::HeapArray<uint8_t> value = MakeValue();
// Fill the cache.
for (const std::string& key : keys) {
ASSERT_THAT(persistent_cache->Insert(key, value.as_span()),
base::test::HasValue());
}
// Shuffle the keys around to avoid taking advantage of file-system caching
// behavior.
base::RandomShuffle(keys.begin(), keys.end());
int success_count = 0;
RunAndTimeTest("Find", kIterationCount, [&] {
success_count = std::ranges::count_if(
keys, [&cache = *persistent_cache](const auto& key) {
return cache.Find(key).has_value();
});
});
ASSERT_EQ(success_count, kIterationCount);
}
#endif // !BUILDFLAG(IS_FUCHSIA)
} // namespace persistent_cache