mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-06 18:10:58 -05:00
* [manunal] Gridstore page use universal IO * fmt * Apply review feedback for universal IO gridstore pages (#8230) * Apply review feedback from PR #8223 - Use `super::Result` import in mmap.rs instead of fully-qualified `crate::universal_io::Result` - Restructure ValuePointer destructuring in get_value and delete_value to first match Some(pointer), then destructure separately * Replace Either<E, GridstoreError> with E: From<GridstoreError> in Gridstore::iter Use a trait bound instead of Either to combine callback and gridstore errors, allowing `?` to work directly on GridstoreError. This simplifies callers by removing Either matching and io::Error conversion workarounds. --------- Co-authored-by: qdrant-claw <qdrant-claw@users.noreply.github.com> --------- Co-authored-by: qdrant-claw <qdrant-claw@users.noreply.github.com>
139 lines
4.4 KiB
Rust
139 lines
4.4 KiB
Rust
use std::hint::black_box;
|
|
use std::io::BufReader;
|
|
use std::path::Path;
|
|
|
|
use common::counter::hardware_counter::HardwareCounterCell;
|
|
use criterion::{Criterion, criterion_group, criterion_main};
|
|
use fs_err as fs;
|
|
use fs_err::File;
|
|
use gridstore::fixtures::{HM_FIELDS, Payload, empty_storage};
|
|
use rand::RngExt;
|
|
use serde_json::Value;
|
|
|
|
/// Insert CSV data into the storage
|
|
fn append_csv_data(storage: &mut gridstore::Gridstore<Payload>, csv_path: &Path) {
|
|
let csv_file = BufReader::new(File::open(csv_path).expect("file should open"));
|
|
let mut rdr = csv::Reader::from_reader(csv_file);
|
|
let mut point_offset = storage.max_point_offset();
|
|
let hw_counter = HardwareCounterCell::new();
|
|
let hw_counter_ref = hw_counter.ref_payload_io_write_counter();
|
|
for result in rdr.records() {
|
|
let record = result.unwrap();
|
|
let mut payload = Payload::default();
|
|
for (i, &field) in HM_FIELDS.iter().enumerate() {
|
|
payload.0.insert(
|
|
field.to_string(),
|
|
Value::String(record.get(i).unwrap().to_string()),
|
|
);
|
|
}
|
|
storage
|
|
.put_value(point_offset, &payload, hw_counter_ref)
|
|
.unwrap();
|
|
point_offset += 1;
|
|
}
|
|
}
|
|
|
|
/// Recursively compute the size of a directory in megabytes
|
|
fn compute_folder_size_mb<P: AsRef<Path>>(path: P) -> u64 {
|
|
let mut size = 0;
|
|
for entry in fs::read_dir(path.as_ref()).unwrap() {
|
|
let entry = entry.unwrap();
|
|
let metadata = entry.metadata().unwrap();
|
|
|
|
if metadata.is_dir() {
|
|
size += compute_folder_size_mb(entry.path());
|
|
} else {
|
|
size += metadata.len();
|
|
}
|
|
}
|
|
(size as f32 / 1_000_000.0).ceil() as u64
|
|
}
|
|
|
|
pub fn real_data_data_bench(c: &mut Criterion) {
|
|
let (dir, mut storage) = empty_storage();
|
|
let csv_path = dataset::Dataset::HMArticles
|
|
.download()
|
|
.expect("download should succeed");
|
|
|
|
// check source file size
|
|
let file_size_bytes = fs::metadata(csv_path.clone())
|
|
.expect("file should exist")
|
|
.len();
|
|
assert_eq!(file_size_bytes, 36_127_865); // 36MB
|
|
|
|
// the CSV file has 105_542 rows
|
|
let expected_point_count = 105_542;
|
|
|
|
// insert data once & flush
|
|
append_csv_data(&mut storage, &csv_path);
|
|
storage.flusher()().unwrap();
|
|
|
|
assert_eq!(storage.max_point_offset(), expected_point_count);
|
|
|
|
// flush to get a consistent bitmask
|
|
storage.flusher()().unwrap();
|
|
|
|
// sanity check of storage size
|
|
let storage_size = storage.get_storage_size_bytes();
|
|
assert_eq!(storage_size, 54_034_048); // 54MB
|
|
|
|
// check storage folder size
|
|
let file_size_mb = compute_folder_size_mb(dir.path());
|
|
assert_eq!(file_size_mb, 70); // 70MB (includes metadata)
|
|
|
|
c.bench_function("compute storage size", |b| {
|
|
b.iter(|| black_box(storage.get_storage_size_bytes()));
|
|
});
|
|
|
|
c.bench_function("scan storage", |b| {
|
|
let hw_counter = HardwareCounterCell::new();
|
|
b.iter(|| {
|
|
for i in 0..storage.max_point_offset() {
|
|
let res = storage.get_value::<false>(i, &hw_counter).unwrap().unwrap();
|
|
assert!(res.0.contains_key("article_id"));
|
|
}
|
|
});
|
|
});
|
|
|
|
// append the same data again to increase storage size
|
|
for _ in 0..10 {
|
|
append_csv_data(&mut storage, &csv_path);
|
|
storage.flusher()().unwrap();
|
|
}
|
|
|
|
let inflated_storage_size = storage.get_storage_size_bytes();
|
|
assert_eq!(inflated_storage_size, 594_374_528); // 594 MB (close to 10x54MB)
|
|
|
|
c.bench_function("compute storage size (large)", |b| {
|
|
b.iter(|| black_box(storage.get_storage_size_bytes()));
|
|
});
|
|
|
|
// delete 30% of the points
|
|
let mut rng = rand::rng();
|
|
for i in 0..storage.max_point_offset() {
|
|
if rng.random_bool(0.3) {
|
|
storage.delete_value(i).unwrap();
|
|
}
|
|
}
|
|
|
|
// flush to get a consistent bitmask
|
|
storage.flusher()().unwrap();
|
|
|
|
c.bench_function("compute storage size (large sparse)", |b| {
|
|
b.iter(|| black_box(storage.get_storage_size_bytes()));
|
|
});
|
|
|
|
c.bench_function("insert real payload (large)", |b| {
|
|
b.iter(|| {
|
|
append_csv_data(&mut storage, &csv_path);
|
|
// do not always flush to build up pending updates
|
|
if rng.random_bool(0.3) {
|
|
storage.flusher()().unwrap();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
criterion_group!(benches, real_data_data_bench);
|
|
criterion_main!(benches);
|