diff --git a/script/bench.py b/script/bench.py index efaac72..34ff7ff 100755 --- a/script/bench.py +++ b/script/bench.py @@ -12,6 +12,8 @@ WRITE_FACTOR = 0.4 READ_FACTOR = 0.2 MIXED_RATIO = 0.5 +SELECTIVITY_FACTOR = ["0.01", "0.1", "0.25"] +RANGE_QUERY_FACTOR = 0.5 PRELOAD = False @@ -27,6 +29,8 @@ def main(args): "file_type": "binary", "seed": 0, "use_preload": PRELOAD, + "range_query_factor": RANGE_QUERY_FACTOR, + "selectivity": SELECTIVITY_FACTOR, } exp_pairs = ((file, index) for file in files for index in INDEXES) diff --git a/script/infra/db.py b/script/infra/db.py index bcc9888..3c1dcb0 100644 --- a/script/infra/db.py +++ b/script/infra/db.py @@ -25,7 +25,10 @@ def __init__(self, db_path: str) -> None: preload_creation_time INT, write_time INT, mixed_time INT, - read_time INT + read_time INT, + range_read_time_short INT, + range_read_time_mid INT, + range_read_time_long INT, ); """ ) @@ -56,8 +59,11 @@ def log_row( preload_creation_time, write_time, mixed_time, - read_time - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + read_time, + range_read_time_short, + range_read_time_mid, + range_read_time_long + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); """, ( datetime.now(timezone.utc), @@ -75,6 +81,9 @@ def log_row( stats.write_time, stats.mixed_time, stats.read_time, + stats.range_read_time_short, + stats.range_read_time_mid, + stats.range_read_time_long, ), ) self.db_con.commit() diff --git a/script/infra/pybliss.py b/script/infra/pybliss.py index 9cc4c53..2faf0d3 100644 --- a/script/infra/pybliss.py +++ b/script/infra/pybliss.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import List import re import os import logging @@ -14,6 +15,8 @@ class BlissArgs: write_factor: float read_factor: float mixed_ratio: float + range_query_factor: float + selectivity: List[str] seed: int = 0 file_type: str = "binary" use_preload: bool = False @@ -26,6 +29,9 @@ class BlissStats: write_time: int read_time: int mixed_time: int + range_read_time_short: int = 0 + range_read_time_mid: int = 0 + range_read_time_long: int = 0 class PyBliss: @@ -51,10 +57,13 @@ def __init__( self.preload_creation_time_regex = re.compile( r"\[[0-9 :.-]+\] \[info\] Preload Creation Time \(ns\): (\d+)" ) + self.range_read_time_regex = re.compile( + r"\[[0-9 :.-]+\] \[info\] Range Query Times \(ns\) for selectivity \[([\d\., ]+)\]: ([\d\., ]+)" + ) def run_single_bliss_bench(self, args: BlissArgs) -> BlissStats: if self.smoke_test: - return BlissStats(*(random.randint(0, 2 << 16) for _ in range(5))) + return BlissStats(*(random.randint(0, 2 << 16) for _ in range(8))) cmd = [ self.bliss_execute_path, @@ -67,6 +76,8 @@ def run_single_bliss_bench(self, args: BlissArgs) -> BlissStats: f"--seed {args.seed}", f"--file_type {'binary' if args.file_type else 'txt'}", "--use_preload" if args.use_preload else "", + f"--selectivity " + ",".join(args.selectivity), + f"--range_query_factor {args.range_query_factor}", ] process = subprocess.Popen( " ".join(cmd), @@ -93,6 +104,23 @@ def run_single_bliss_bench(self, args: BlissArgs) -> BlissStats: read_time = self.read_time_regex.search(proc_results) read_time = int(read_time.group(1)) if read_time else 0 + # Extract range query times from the comma-separated list + range_times_match = self.range_read_time_regex.search(proc_results) + range_read_time_short = 0 + range_read_time_mid = 0 + range_read_time_long = 0 + + if range_times_match: + range_times_str = range_times_match.group(2) + range_times = [int(x.strip()) for x in range_times_str.split(',')] + + if len(range_times) >= 1: + range_read_time_short = range_times[0] + if len(range_times) >= 2: + range_read_time_mid = range_times[1] + if len(range_times) >= 3: + range_read_time_long = range_times[2] + os.makedirs("./run_logs", exist_ok=True) _, file_name = os.path.split(args.data_file) file_name, _ = os.path.splitext(file_name + f"_{args.index_type}") @@ -105,4 +133,7 @@ def run_single_bliss_bench(self, args: BlissArgs) -> BlissStats: write_time=write_time, read_time=read_time, mixed_time=mixed_time, + range_read_time_short=range_read_time_short, + range_read_time_mid=range_read_time_mid, + range_read_time_long=range_read_time_long, ) diff --git a/src/bliss/bench_alex.h b/src/bliss/bench_alex.h index a8a5bfb..8266841 100644 --- a/src/bliss/bench_alex.h +++ b/src/bliss/bench_alex.h @@ -14,7 +14,7 @@ template class BlissAlexIndex : public BlissIndex { public: alex::Alex _index; - BlissAlexIndex() : _index(){}; + BlissAlexIndex() : _index() {}; void bulkload( std::vector> values) override { diff --git a/src/bliss/util/args.h b/src/bliss/util/args.h index cbebc83..d5d55b4 100644 --- a/src/bliss/util/args.h +++ b/src/bliss/util/args.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "bliss/util/config.h" @@ -36,9 +37,24 @@ BlissConfig parse_args(int argc, char *argv[]) { "file_type", "Input file type [binary | txt]", cxxopts::value()->default_value("txt"))( "use_preload", "Use index defined preload", - cxxopts::value()->default_value("false")); + cxxopts::value()->default_value("false"))( + "range_query_perc", "Range query factor", + cxxopts::value()->default_value("0.0"))( + "selectivity", "Selectivity factor(s) (comma-separated, percentage of domain)", + cxxopts::value()->default_value("0.01")); auto result = options.parse(argc, argv); + + // Parse selectivity factors + std::vector selectivities; + std::string selectivity_str = result["selectivity"].as(); + size_t start = 0, end = 0; + while ((end = selectivity_str.find(',', start)) != std::string::npos) { + selectivities.push_back(std::stod(selectivity_str.substr(start, end - start))); + start = end + 1; + } + selectivities.push_back(std::stod(selectivity_str.substr(start))); + config = { .data_file = result["data_file"].as(), .preload_factor = result["preload_factor"].as(), @@ -51,6 +67,8 @@ BlissConfig parse_args(int argc, char *argv[]) { .index = result["index"].as(), .file_type = result["file_type"].as(), .use_preload = result["use_preload"].as(), + .range_query_perc = result["range_query_perc"].as(), + .selectivity_factor = selectivities, }; } catch (const std::exception &e) { std::cerr << "Error: " << e.what() << std::endl; diff --git a/src/bliss/util/config.h b/src/bliss/util/config.h index d3d56c6..f309abc 100644 --- a/src/bliss/util/config.h +++ b/src/bliss/util/config.h @@ -4,6 +4,7 @@ #include #include +#include namespace bliss { namespace utils { @@ -19,6 +20,8 @@ struct BlissConfig { std::string index; std::string file_type; bool use_preload; + double range_query_perc = 0.0; + std::vector selectivity_factor = {0.01}; }; void display_config(BlissConfig config) { @@ -30,6 +33,14 @@ void display_config(BlissConfig config) { spdlog::trace("Verbosity {}", config.verbosity); spdlog::trace("Index: {}", config.index); spdlog::trace("File type: {}", config.file_type); + spdlog::trace("Use Preload: {}", config.use_preload); + spdlog::trace("Range Query Factor: {}", config.range_query_perc); + std::string selectivities; + for (size_t i = 0; i < config.selectivity_factor.size(); ++i) { + selectivities += std::to_string(config.selectivity_factor[i]); + if (i != config.selectivity_factor.size() - 1) selectivities += ", "; + } + spdlog::trace("Selectivity Factor(s): {}", selectivities); } } // namespace config } // namespace utils diff --git a/src/bliss/util/execute.h b/src/bliss/util/execute.h index 5963c55..44daf0d 100644 --- a/src/bliss/util/execute.h +++ b/src/bliss/util/execute.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "bliss/bliss_index.h" @@ -43,6 +44,26 @@ void execute_non_empty_reads(bliss::BlissIndex &tree, } } +void execute_range_queries(bliss::BlissIndex &tree, + const std::vector &data, int num_queries, + double selectivity = 0.01, int seed = 0) { + spdlog::trace("Executing Range Queries"); + std::mt19937 gen(seed); + std::uniform_int_distribution key_dist(0, data.size() - 1); + + key_type selected_data_range = *std::max_element(data.begin(), data.end()) - + *std::min_element(data.begin(), data.end()); + key_type avg_range_size = static_cast(selected_data_range * selectivity); + + for (int i = 0; i < num_queries; ++i) { + size_t start_idx = key_dist(gen); + key_type start_key = data.at(start_idx); + key_type end_key = start_key + avg_range_size; + + tree.get(start_key, end_key); + } +} + } // namespace executor } // namespace utils } // namespace bliss diff --git a/src/bliss_bench.cpp b/src/bliss_bench.cpp index ddb2f25..cb6a202 100644 --- a/src/bliss_bench.cpp +++ b/src/bliss_bench.cpp @@ -90,6 +90,7 @@ void workload_executor(bliss::BlissIndex &tree, size_t num_writes = std::round(config.write_factor * data.size()); size_t num_mixed = num_inserts - (num_preload + num_writes); size_t num_reads = std::round(config.read_factor * data.size()); + size_t num_ranges = std::round(config.range_query_perc * data.size()); // Timing for preloading index spdlog::debug("Preloading {} items", num_preload); @@ -143,8 +144,47 @@ void workload_executor(bliss::BlissIndex &tree, executor::execute_non_empty_reads(tree, data, num_reads, seed); }); spdlog::info("Read Time (ns): {}", read_time); + + // Timing for range queries with configured amount + if (num_ranges > 0) { + spdlog::debug("Executing {} range queries", num_ranges); + std::vector range_times; + std::string selectivity_values; + + // Process all selectivity factors first + for (const auto& selectivity : config.selectivity_factor) { + auto range_time = 0ULL; + try { + range_time = time_function([&]() { + executor::execute_range_queries(tree, data, num_ranges, selectivity); + }); + } catch (const std::exception& e) { + if (std::string(e.what()) == "Not implemented") { + range_time = 0; + } else { + throw; + } + } + + range_times.push_back(range_time); + if (!selectivity_values.empty()) { + selectivity_values += ", "; + } + selectivity_values += std::to_string(selectivity); + } + + std::string time_values; + for (size_t i = 0; i < range_times.size(); ++i) { + if (i > 0) time_values += ", "; + time_values += std::to_string(range_times[i]); + } + + spdlog::info("Range Query Times (ns) for selectivity [{}]: {}", + selectivity_values, time_values); + } } + int main(int argc, char *argv[]) { auto config = args::parse_args(argc, argv); switch (config.verbosity) { diff --git a/tests/test_alex/alex_tests.cpp b/tests/test_alex/alex_tests.cpp index c7e3fc6..d84e60c 100644 --- a/tests/test_alex/alex_tests.cpp +++ b/tests/test_alex/alex_tests.cpp @@ -1,6 +1,27 @@ #include "bliss_index_tests.h" class AlexTest : public BlissIndexTest {}; + +TEST_F(AlexTest, TestAlex_RangeQuery) { + index.reset(new bliss::BlissAlexIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(AlexTest, TestAlex_Sorted) { index.reset(new bliss::BlissAlexIndex()); std::vector data; diff --git a/tests/test_art/art_tests.cpp b/tests/test_art/art_tests.cpp index 7db0f7b..159784b 100644 --- a/tests/test_art/art_tests.cpp +++ b/tests/test_art/art_tests.cpp @@ -11,6 +11,26 @@ TEST_F(ArtTest, TestArt_Sanity) { EXPECT_TRUE(index->get(key)); } +TEST_F(ArtTest, TestArt_RangeQuery) { + index.reset(new bliss::BlissARTIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(ArtTest, TestArt_Sorted) { index.reset(new bliss::BlissARTIndex()); std::vector data; diff --git a/tests/test_btree/btree_tests.cpp b/tests/test_btree/btree_tests.cpp index 91f9833..e1ea884 100644 --- a/tests/test_btree/btree_tests.cpp +++ b/tests/test_btree/btree_tests.cpp @@ -11,6 +11,26 @@ TEST_F(BTreeTest, TestBtree_Sanity) { EXPECT_TRUE(index->get(key)); } +TEST_F(BTreeTest, TestBtree_RangeQuery) { + index.reset(new bliss::BlissBTreeIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(BTreeTest, TestBtree_Sorted) { index.reset(new bliss::BlissBTreeIndex()); std::vector data; diff --git a/tests/test_leveldb/leveldb_tests.cpp b/tests/test_leveldb/leveldb_tests.cpp index 82882a3..9c88006 100644 --- a/tests/test_leveldb/leveldb_tests.cpp +++ b/tests/test_leveldb/leveldb_tests.cpp @@ -11,6 +11,26 @@ TEST_F(LevelDBTest, TestLevelDB_Sanity) { EXPECT_TRUE(index->get(key)); } +TEST_F(LevelDBTest, TestLevelDB_RangeQuery) { + index.reset(new bliss::BlissLevelDBIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(LevelDBTest, TestLevelDB_Sorted) { index.reset(new bliss::BlissLevelDBIndex()); std::vector data; diff --git a/tests/test_lipp/lipp_tests.cpp b/tests/test_lipp/lipp_tests.cpp index 146a842..63a3754 100644 --- a/tests/test_lipp/lipp_tests.cpp +++ b/tests/test_lipp/lipp_tests.cpp @@ -2,6 +2,26 @@ class LippTest : public BlissIndexTest {}; +TEST_F(LippTest, TestLipp_RangeQuery) { + index.reset(new bliss::BlissLippIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(LippTest, TestLipp_Sorted) { index.reset(new bliss::BlissLippIndex()); std::vector data; diff --git a/tests/test_pgm/pgm_tests.cpp b/tests/test_pgm/pgm_tests.cpp index 32b0302..0a00736 100644 --- a/tests/test_pgm/pgm_tests.cpp +++ b/tests/test_pgm/pgm_tests.cpp @@ -11,6 +11,26 @@ TEST_F(PGMTest, TestPGM_Sanity) { EXPECT_TRUE(index->get(key)); } +TEST_F(PGMTest, TestPGM_RangeQuery) { + index.reset(new bliss::PGMIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(PGMTest, TestPGM_Sorted) { index.reset(new bliss::PGMIndex()); std::vector data; diff --git a/tests/test_skiplist/skiplist_tests.cpp b/tests/test_skiplist/skiplist_tests.cpp index 2de3cb2..70a89f3 100644 --- a/tests/test_skiplist/skiplist_tests.cpp +++ b/tests/test_skiplist/skiplist_tests.cpp @@ -11,6 +11,26 @@ TEST_F(SkipListTest, TestSkipList_Sanity) { EXPECT_TRUE(index->get(key)); } +TEST_F(SkipListTest, TestSkipList_RangeQuery) { + index.reset(new bliss::BlissSkipListIndex()); + std::vector data; + GenerateData(data, num_keys); + + auto insert_start = data.begin(); + auto insert_end = data.end(); + executor::execute_inserts(*index, insert_start, insert_end); + + key_type start_key = data.front(); + key_type end_key = start_key + (data.back() - data.front()) / 4; + + try { + index->get(start_key, end_key); + SUCCEED(); + } catch (const std::runtime_error& e) { + EXPECT_STREQ("Not implemented", e.what()); + } +} + TEST_F(SkipListTest, TestSkipList_Sorted) { index.reset(new bliss::BlissSkipListIndex()); std::vector data;