#include #include #include #include #include "leveldb/env.h" #include "leveldb/db.h" #include "db/fields.h" #include "leveldb/write_batch.h" using namespace leveldb; // Number of key/values to operate in database constexpr int num_ = 500000; // Size of each value constexpr int value_size_ = 1024; // Number of read operations constexpr int reads_ = 500000; Status OpenDB(std::string dbName, DB **db) { Options options; options.create_if_missing = true; return DB::Open(options, dbName, db); } void InsertData(DB *db, std::vector &lats) { WriteOptions writeOptions; srand(0); for (int i = 0; i < num_; ++i) { int key_ = rand() % num_ + 1; std::string key = std::to_string(key_); std::string value(value_size_, 'a'); auto start_time = std::chrono::steady_clock::now(); db->Put(writeOptions, key, value); auto end_time = std::chrono::steady_clock::now(); lats.emplace_back(std::chrono::duration_cast(end_time - start_time).count()); } } void InsertFields(DB *db, std::vector &lats) { WriteOptions writeOptions; srand(0); for (int i = 0; i < num_; ++i) { int key_ = rand() % num_ + 1; std::string key = std::to_string(key_); FieldArray fields = {{"field", "old_value_"}}; Fields f(fields); auto start_time = std::chrono::steady_clock::now(); db->PutFields(writeOptions, Slice(key), f); auto end_time = std::chrono::steady_clock::now(); lats.emplace_back(std::chrono::duration_cast(end_time - start_time).count()); } } void GetData(DB *db, std::vector &lats) { ReadOptions readOptions; srand(0); for (int i = 0; i < reads_; ++i) { int key_ = rand() % num_ + 1; std::string key = std::to_string(key_); std::string value; auto start_time = std::chrono::steady_clock::now(); db->Get(readOptions, key, &value); auto end_time = std::chrono::steady_clock::now(); lats.emplace_back(std::chrono::duration_cast(end_time - start_time).count()); } } void ReadOrdered(DB *db, std::vector &lats) { Iterator* iter = db->NewIterator(ReadOptions()); int i = 0; for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) { ++i; auto start_time = std::chrono::steady_clock::now(); // Just iterating over the data without performing any operation. auto end_time = std::chrono::steady_clock::now(); lats.emplace_back(std::chrono::duration_cast(end_time - start_time).count()); } delete iter; } void FindKeys(DB *db, std::vector &lats) { srand(0); for (int i = 0; i < reads_; ++i) { int key_ = rand() % num_ + 1; FieldArray fields_to_find = {{"field", "old_value_" }}; auto start_time = std::chrono::steady_clock::now(); std::string dbname_ = "bench_resr_db"; Options options; options.create_if_missing = true; DBImpl* impl = new DBImpl(options, dbname_); Fields::FindKeysByFields(db, fields_to_find, impl); auto end_time = std::chrono::steady_clock::now(); lats.emplace_back(std::chrono::duration_cast(end_time - start_time).count()); } } double CalculatePercentile(const std::vector& latencies, double percentile) { if (latencies.empty()) return 0.0; std::vector sorted_latencies = latencies; std::sort(sorted_latencies.begin(), sorted_latencies.end()); size_t index = static_cast(percentile * sorted_latencies.size()); if (index >= sorted_latencies.size()) index = sorted_latencies.size() - 1; return sorted_latencies[index]; } void SetupData(DB *db) { std::vector lats; InsertData(db, lats); } void SetupFields(DB *db) { std::vector lats; InsertFields(db, lats); } template void RunBenchmark(const char* name, Func func, bool setup_data = true, bool setup_fields = false) { DB *db; std::string rm_command = "rm -rf testdb_bench"; system(rm_command.c_str()); if (!OpenDB("testdb_bench", &db).ok()) { std::cerr << "open db failed" << std::endl; abort(); } if (setup_data) SetupData(db); if (setup_fields) SetupFields(db); std::vector lats; auto start_time = std::chrono::steady_clock::now(); func(db, lats); auto end_time = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end_time - start_time).count(); double avg = 0.0; for (auto latency : lats) { avg += latency; } avg /= lats.size(); double p75 = CalculatePercentile(lats, 0.75); double p99 = CalculatePercentile(lats, 0.99); std::cout << name << " Latency (avg, P75, P99): " << avg << " micros/op, " << p75 << " micros/op, " << p99 << " micros/op" << std::endl; std::cout << name << " Throughput: " << lats.size() / duration << " ops/ms" << std::endl; delete db; } TEST(BenchTest, PutLatency) { RunBenchmark("Put", InsertData, false, false); } TEST(BenchTest, PutFieldsLatency) { RunBenchmark("PutFields", InsertFields, false, false); } TEST(BenchTest, GetLatency) { RunBenchmark("Get", GetData, true, false); } TEST(BenchTest, IteratorLatency) { RunBenchmark("Iterator", ReadOrdered, true, false); } TEST(BenchTest, FindKeysByFieldLatency) { RunBenchmark("FindKeysByFields", FindKeys, false, true); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }