Skip to content

Commit 8ef7443

Browse files
committed
examples: add dynamic data project
1 parent a5f682e commit 8ef7443

10 files changed

Lines changed: 2192 additions & 22 deletions

File tree

cpp/bench/include/build.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,25 @@ inline void create_graph(const deglib::StaticFeatureRepository& repository,
160160

161161
if (data_stream_type == DataStreamType::AddHalfRemoveAndAddOneAtATime) {
162162
auto base_size_half = base_size / 2;
163-
auto base_size_fourth = base_size / 4;
164-
for (uint32_t i = 0; i < base_size_fourth; i++) {
163+
auto base_size_quarter = base_size / 4;
164+
// First loop: add base_size_quarter pairs from [0, base_size_quarter) and [base_size_half, base_size_half + base_size_quarter)
165+
for (uint32_t i = 0; i < base_size_quarter; i++) {
165166
addEntry(0 + i);
166167
addEntry(base_size_half + i);
167168
}
168-
for (uint32_t i = 0; i < base_size_fourth; i++) {
169-
addEntry(base_size_fourth + i);
170-
addEntry(base_size_half + base_size_fourth + i);
169+
// Second loop: add base_size_quarter pairs from [base_size_quarter, base_size_half) and remove the same number
170+
// to keep exactly base_size_half vertices in the graph
171+
for (uint32_t i = 0; i < base_size_quarter; i++) {
172+
addEntry(base_size_quarter + i);
173+
addEntry(base_size_half + base_size_quarter + i);
171174
builder.removeEntry(base_size_half + (i * 2) + 0);
172175
builder.removeEntry(base_size_half + (i * 2) + 1);
173176
}
177+
// If base_size is not divisible by 4, add the remaining entries to reach exactly base_size_half vertices
178+
auto remainder = base_size_half - (base_size_quarter * 2);
179+
for (uint32_t i = 0; i < remainder; i++) {
180+
addEntry(base_size_quarter * 2 + i);
181+
}
174182
} else {
175183
base_size /= (data_stream_type == DataStreamType::AddHalf) ? 2 : 1;
176184
for (uint32_t i = 0; i < base_size; i++) addEntry(i);

cpp/bench/src/bench_dynamic_data.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ struct DynamicConfig {
2525
uint8_t k_ext = 60;
2626
float eps_ext = 0.1f;
2727
deglib::builder::OptimizationTarget lid = deglib::builder::OptimizationTarget::StreamingData;
28-
uint32_t build_threads = std::thread::hardware_concurrency() / 2;
2928
uint32_t anns_k = 100;
3029
uint32_t anns_repeat = 1;
3130
uint32_t anns_threads = 1;
@@ -91,7 +90,6 @@ void print_help(const char* program_name) {
9190
log("Options:\n");
9291
log(" --force-rebuild Force rebuilding the graphs even if graph files already exist\n");
9392
log(" --instruction <inst> Select distance instruction set (auto, avx512, avx2, scalar)\n");
94-
log(" --threads <count> Number of threads used for building the graph (default: hardware_concurrency / 2)\n");
9593
log(" --help, -h Display this detailed help message\n\n");
9694
log("Data Path:\n");
9795
log(" Data is loaded from / saved to DATA_PATH defined at build time (e.g. \"{}\")\n", DATA_PATH);
@@ -106,10 +104,9 @@ static deglib::cpu::InstructionSet parse_instruction_set(const std::string& str)
106104
return deglib::cpu::InstructionSet::Auto;
107105
}
108106

109-
void run_dynamic_benchmark(const DatasetName& ds_name, const std::filesystem::path& data_path, bool force_rebuild, deglib::cpu::InstructionSet instruction, uint32_t build_threads) {
107+
void run_dynamic_benchmark(const DatasetName& ds_name, const std::filesystem::path& data_path, bool force_rebuild, deglib::cpu::InstructionSet instruction) {
110108
Dataset ds(ds_name, data_path);
111109
auto config = get_dataset_config(ds_name);
112-
config.build_threads = build_threads;
113110

114111
log("\n=== Benchmarking Dynamic Data Streams for Dataset: {} ===\n", ds.name());
115112

@@ -149,7 +146,7 @@ void run_dynamic_benchmark(const DatasetName& ds_name, const std::filesystem::pa
149146
config.k_ext,
150147
config.eps_ext,
151148
0, 0, 0,
152-
config.build_threads,
149+
1,
153150
true,
154151
ds.info().scale,
155152
false,
@@ -190,10 +187,9 @@ void run_dynamic_benchmark(const DatasetName& ds_name, const std::filesystem::pa
190187

191188
int main(int argc, char* argv[]) {
192189
const auto data_path = std::filesystem::path(DATA_PATH);
193-
DatasetName ds_name = DatasetName::SIFT1M;
190+
DatasetName ds_name = DatasetName::AUDIO;
194191
bool force_rebuild = false;
195-
deglib::cpu::InstructionSet instruction = deglib::cpu::InstructionSet::Auto;
196-
uint32_t build_threads = std::thread::hardware_concurrency() / 2;
192+
deglib::cpu::InstructionSet instruction = deglib::cpu::InstructionSet::AVX2;
197193

198194
for (int i = 1; i < argc; ++i) {
199195
std::string arg = argv[i];
@@ -204,8 +200,6 @@ int main(int argc, char* argv[]) {
204200
force_rebuild = true;
205201
} else if (arg == "--instruction" && i + 1 < argc) {
206202
instruction = parse_instruction_set(argv[++i]);
207-
} else if (arg == "--threads" && i + 1 < argc) {
208-
build_threads = (uint32_t)std::stoul(argv[++i]);
209203
} else {
210204
auto parsed = DatasetName::from_string(arg);
211205
if (parsed.is_valid()) {
@@ -228,7 +222,7 @@ int main(int argc, char* argv[]) {
228222
}
229223

230224
for (const auto& current_ds : datasets_to_run) {
231-
run_dynamic_benchmark(current_ds, data_path, force_rebuild, instruction, build_threads);
225+
run_dynamic_benchmark(current_ds, data_path, force_rebuild, instruction);
232226
}
233227

234228
log("\nDynamic Benchmark Finished Successfully.\n");

examples/dynamic_data/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# DEG Dynamic Data Benchmark (Python)
2+
3+
Python mirror of `cpp/bench/src/bench_dynamic_data.cpp`.
4+
5+
Builds and evaluates DEG graphs under three dynamic data streaming conditions and measures ANNS recall vs. QPS performance against the **half-dataset ground truth** (first half of base vectors).
6+
7+
## Tested Data Stream Patterns
8+
9+
| Stream Type | Description |
10+
|---|---|
11+
| `AddHalf` | Insert only the first half of base vectors |
12+
| `AddHalfRemoveAndAddOneAtATime` | Interleaved insertion and deletion operations |
13+
| `AddAllRemoveHalf` | Insert all vectors, then remove the second half |
14+
15+
All three use `OptimizationTarget.StreamingData`.
16+
17+
## Usage
18+
19+
```bash
20+
uv run python main.py [dataset] [options]
21+
```
22+
23+
### Datasets
24+
- `sift1m` — SIFT1M (1M vectors, 128D, default)
25+
- `deep1m` — DEEP1M (1M vectors, 96D)
26+
- `glove` / `glove-100` — GloVe (1.18M vectors, 100D)
27+
- `audio` — Audio (53.3k vectors, 192D)
28+
- `enron` — Enron (94.9k vectors, 1369D)
29+
- `all` — Run all datasets sequentially
30+
31+
### Options
32+
33+
| Option | Description |
34+
|---|---|
35+
| `--graph-dir <path>` | Directory to save/load graph files. Filenames follow C++ naming: `{dims}D_K{k}_{stream_type}.deg` |
36+
| `--force-rebuild` | Rebuild graphs even if files already exist |
37+
| `--instruction <inst>` | Distance instruction set: `auto`, `avx512`, `avx2`, `scalar` |
38+
| `--threads <n>` | Build threads (default: 1) |
39+
| `--cache-dir <path>` | Dataset cache directory (default: `~/.cache/deg_datasets`) |
40+
| `--no-show` | Do not display interactive plots |
41+
| `--max-base-vecs <n>` | Limit base vectors for quick testing |
42+
43+
### Examples
44+
45+
```bash
46+
# Quick test with audio dataset
47+
uv run python main.py audio --max-base-vecs 5000 --no-show
48+
49+
# Full SIFT1M benchmark, saving graphs to disk
50+
uv run python main.py sift1m --graph-dir /data/deg_graphs/sift1m/dynamic
51+
52+
# All datasets
53+
uv run python main.py all --graph-dir /data/deg_graphs --threads 4
54+
```
55+
56+
## Setup
57+
58+
```bash
59+
uv sync
60+
```
61+
62+
## Key Differences from `static_data`
63+
64+
- Iterates over 3 `DataStreamType`s instead of a single `AddAll`
65+
- Uses `StreamingData` optimization target for all datasets
66+
- ANNS test uses **half-dataset ground truth** (queries evaluated against first `base_count/2` vectors)
67+
- No exploration test
68+
- Graph files named `{dims}D_K{k}_{stream_type}.deg` (C++ compatible)

0 commit comments

Comments
 (0)