Skip to content

Commit 0b18de9

Browse files
committed
feat(object): implement multi-tier adaptive optimization for Object::get
- Add size-based adaptive search strategies
1 parent 41ae6e8 commit 0b18de9

File tree

5 files changed

+455
-3
lines changed

5 files changed

+455
-3
lines changed

benchmarks/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,9 @@ name = "get_from"
6262
harness = false
6363
name = "value_operator"
6464

65+
[[bench]]
66+
harness = false
67+
name = "object_get_optimization"
68+
6569
[features]
6670
default = []
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
2+
use sonic_rs::{Object, Value, from_str};
3+
4+
fn create_small_object(size: usize) -> Object {
5+
let mut obj = Object::new();
6+
for i in 0..size {
7+
obj.insert(&format!("key{:02}", i), Value::from(i));
8+
}
9+
obj
10+
}
11+
12+
fn create_medium_object(size: usize) -> Object {
13+
let mut obj = Object::new();
14+
for i in 0..size {
15+
// Use longer keys to benefit from SIMD optimization
16+
obj.insert(&format!("medium_key_name_{:03}", i), Value::from(i));
17+
}
18+
obj
19+
}
20+
21+
fn create_large_object(size: usize) -> Object {
22+
let mut obj = Object::new();
23+
for i in 0..size {
24+
obj.insert(&format!("large_object_key_{:04}", i), Value::from(i * 10));
25+
}
26+
obj
27+
}
28+
29+
fn bench_small_objects(c: &mut Criterion) {
30+
let core_ids = core_affinity::get_core_ids().unwrap();
31+
core_affinity::set_for_current(core_ids[0]);
32+
33+
let mut group = c.benchmark_group("small_objects_1_to_7_keys");
34+
35+
for size in [1, 3, 5, 7] {
36+
let obj = create_small_object(size);
37+
let test_key = format!("key{:02}", size / 2); // middle key
38+
39+
group.throughput(Throughput::Elements(1));
40+
group.bench_with_input(
41+
BenchmarkId::new("optimized_get", size),
42+
&(obj, test_key),
43+
|b, (obj, key)| {
44+
b.iter(|| obj.get(key))
45+
}
46+
);
47+
}
48+
group.finish();
49+
}
50+
51+
fn bench_medium_objects(c: &mut Criterion) {
52+
let core_ids = core_affinity::get_core_ids().unwrap();
53+
core_affinity::set_for_current(core_ids[0]);
54+
55+
let mut group = c.benchmark_group("medium_objects_8_to_31_keys");
56+
57+
for size in [8, 15, 20, 31] {
58+
let obj = create_medium_object(size);
59+
let test_key = format!("medium_key_name_{:03}", size / 2); // middle key
60+
61+
group.throughput(Throughput::Elements(1));
62+
group.bench_with_input(
63+
BenchmarkId::new("simd_optimized_get", size),
64+
&(obj, test_key),
65+
|b, (obj, key)| {
66+
b.iter(|| obj.get(key))
67+
}
68+
);
69+
}
70+
group.finish();
71+
}
72+
73+
fn bench_large_objects(c: &mut Criterion) {
74+
let core_ids = core_affinity::get_core_ids().unwrap();
75+
core_affinity::set_for_current(core_ids[0]);
76+
77+
let mut group = c.benchmark_group("large_objects_32_plus_keys");
78+
79+
for size in [32, 50, 100, 200] {
80+
let obj = create_large_object(size);
81+
let test_key = format!("large_object_key_{:04}", size / 2); // middle key
82+
83+
group.throughput(Throughput::Elements(1));
84+
group.bench_with_input(
85+
BenchmarkId::new("hash_index_get", size),
86+
&(obj, test_key),
87+
|b, (obj, key)| {
88+
b.iter(|| obj.get(key))
89+
}
90+
);
91+
}
92+
group.finish();
93+
}
94+
95+
fn bench_different_key_positions(c: &mut Criterion) {
96+
let core_ids = core_affinity::get_core_ids().unwrap();
97+
core_affinity::set_for_current(core_ids[0]);
98+
99+
let mut group = c.benchmark_group("key_position_impact");
100+
101+
let obj = create_large_object(100);
102+
103+
// Test first, middle, and last key positions
104+
let positions = [
105+
("first", "large_object_key_0000"),
106+
("middle", "large_object_key_0050"),
107+
("last", "large_object_key_0099"),
108+
];
109+
110+
for (pos_name, key) in positions {
111+
group.bench_with_input(
112+
BenchmarkId::new("get_by_position", pos_name),
113+
&key,
114+
|b, key| {
115+
b.iter(|| obj.get(key))
116+
}
117+
);
118+
}
119+
group.finish();
120+
}
121+
122+
fn bench_cache_behavior(c: &mut Criterion) {
123+
let core_ids = core_affinity::get_core_ids().unwrap();
124+
core_affinity::set_for_current(core_ids[0]);
125+
126+
let mut group = c.benchmark_group("cache_behavior");
127+
128+
let obj = create_large_object(100);
129+
let test_key = "large_object_key_0050";
130+
131+
group.bench_function("repeated_lookups", |b| {
132+
b.iter(|| {
133+
// Perform multiple lookups to test cache effectiveness
134+
for _ in 0..10 {
135+
obj.get(&test_key);
136+
}
137+
})
138+
});
139+
140+
group.finish();
141+
}
142+
143+
fn bench_key_length_impact(c: &mut Criterion) {
144+
let core_ids = core_affinity::get_core_ids().unwrap();
145+
core_affinity::set_for_current(core_ids[0]);
146+
147+
let mut group = c.benchmark_group("key_length_impact");
148+
149+
// Test with different key lengths to evaluate SIMD effectiveness
150+
let test_cases = [
151+
("short", 4, "k"),
152+
("medium", 16, "medium_length_key"),
153+
("long", 32, "very_long_key_name_that_should_benefit_from_simd"),
154+
];
155+
156+
for (name, obj_size, key_prefix) in test_cases {
157+
let mut obj = Object::new();
158+
for i in 0..obj_size {
159+
let key = format!("{}_{:03}", key_prefix, i);
160+
obj.insert(&key, Value::from(i));
161+
}
162+
163+
let test_key = format!("{}_{:03}", key_prefix, obj_size / 2);
164+
165+
group.bench_with_input(
166+
BenchmarkId::new("get_by_key_length", name),
167+
&(obj, test_key),
168+
|b, (obj, key)| {
169+
b.iter(|| obj.get(key))
170+
}
171+
);
172+
}
173+
group.finish();
174+
}
175+
176+
fn bench_real_world_patterns(c: &mut Criterion) {
177+
let core_ids = core_affinity::get_core_ids().unwrap();
178+
core_affinity::set_for_current(core_ids[0]);
179+
180+
let mut group = c.benchmark_group("real_world_patterns");
181+
182+
// Simulate common JSON patterns
183+
let json_configs = [
184+
("api_response", r#"{"status": "success", "data": {"id": 123, "name": "John", "email": "[email protected]"}, "timestamp": "2024-01-01T00:00:00Z", "version": "1.0"}"#),
185+
("user_profile", r#"{"userId": 12345, "username": "johndoe", "firstName": "John", "lastName": "Doe", "email": "[email protected]", "birthDate": "1990-01-01", "isActive": true, "roles": ["user", "admin"], "preferences": {"theme": "dark", "language": "en"}, "lastLogin": "2024-01-01T12:00:00Z"}"#),
186+
];
187+
188+
for (name, json) in json_configs {
189+
let obj: Object = from_str(json).unwrap();
190+
191+
// Test common access patterns
192+
let test_keys = match name {
193+
"api_response" => vec!["status", "data", "timestamp"],
194+
"user_profile" => vec!["userId", "email", "isActive", "preferences"],
195+
_ => vec!["status"],
196+
};
197+
198+
for key in test_keys {
199+
group.bench_with_input(
200+
BenchmarkId::new(name, key),
201+
&(obj.clone(), key),
202+
|b, (obj, key)| {
203+
b.iter(|| obj.get(key))
204+
}
205+
);
206+
}
207+
}
208+
group.finish();
209+
}
210+
211+
criterion_group!(
212+
benches,
213+
bench_small_objects,
214+
bench_medium_objects,
215+
bench_large_objects,
216+
bench_different_key_positions,
217+
bench_cache_behavior,
218+
bench_key_length_impact,
219+
bench_real_world_patterns
220+
);
221+
criterion_main!(benches);

0 commit comments

Comments
 (0)