Skip to main content

validate_named_bench/
validate_named_bench.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9use std::hint::black_box;
10use std::sync::Arc;
11use std::time::Instant;
12
13use qubit_validator::ArgumentReader;
14use qubit_validator::BindError;
15use qubit_validator::BoundValidationContext;
16use qubit_validator::BoundValidator;
17use qubit_validator::DependencySpec;
18use qubit_validator::ExecutionError;
19use qubit_validator::InputType;
20use qubit_validator::NamedValidationDependency;
21use qubit_validator::PreparedOutcome;
22use qubit_validator::PreparedValidator;
23use qubit_validator::ValidationOutcome;
24use qubit_validator::ValidationPath;
25use qubit_validator::ValidationValue;
26use qubit_validator::ValidatorDescriptor;
27use qubit_validator::ValidatorId;
28use qubit_validator::ValidatorSignature;
29
30struct Accept {
31    dependencies: &'static [DependencySpec],
32}
33
34impl PreparedValidator for Accept {
35    fn input_type(&self) -> InputType {
36        InputType::Text
37    }
38
39    fn dependency_specs(&self) -> &'static [DependencySpec] {
40        self.dependencies
41    }
42
43    fn validate(
44        &self,
45        _: ValidationValue<'_>,
46        _: &BoundValidationContext<'_>,
47    ) -> Result<PreparedOutcome, ExecutionError> {
48        Ok(PreparedOutcome::valid())
49    }
50}
51
52static D0: &[DependencySpec] = &[];
53static D1: &[DependencySpec] = &[DependencySpec::new("a", InputType::of::<u64>(), false)];
54static D4: &[DependencySpec] = &[
55    DependencySpec::new("a", InputType::of::<u64>(), false),
56    DependencySpec::new("b", InputType::of::<u64>(), false),
57    DependencySpec::new("c", InputType::of::<u64>(), false),
58    DependencySpec::new("d", InputType::of::<u64>(), false),
59];
60static D8: &[DependencySpec] = &[
61    DependencySpec::new("a", InputType::of::<u64>(), false),
62    DependencySpec::new("b", InputType::of::<u64>(), false),
63    DependencySpec::new("c", InputType::of::<u64>(), false),
64    DependencySpec::new("d", InputType::of::<u64>(), false),
65    DependencySpec::new("e", InputType::of::<u64>(), false),
66    DependencySpec::new("f", InputType::of::<u64>(), false),
67    DependencySpec::new("g", InputType::of::<u64>(), false),
68    DependencySpec::new("h", InputType::of::<u64>(), false),
69];
70static D9: &[DependencySpec] = &[
71    DependencySpec::new("a", InputType::of::<u64>(), false),
72    DependencySpec::new("b", InputType::of::<u64>(), false),
73    DependencySpec::new("c", InputType::of::<u64>(), false),
74    DependencySpec::new("d", InputType::of::<u64>(), false),
75    DependencySpec::new("e", InputType::of::<u64>(), false),
76    DependencySpec::new("f", InputType::of::<u64>(), false),
77    DependencySpec::new("g", InputType::of::<u64>(), false),
78    DependencySpec::new("h", InputType::of::<u64>(), false),
79    DependencySpec::new("i", InputType::of::<u64>(), false),
80];
81
82fn prepare0(_reader: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
83    Ok(Arc::new(Accept { dependencies: D0 }))
84}
85
86fn prepare1(_reader: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
87    Ok(Arc::new(Accept { dependencies: D1 }))
88}
89
90fn prepare4(_reader: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
91    Ok(Arc::new(Accept { dependencies: D4 }))
92}
93
94fn prepare8(_reader: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
95    Ok(Arc::new(Accept { dependencies: D8 }))
96}
97
98fn prepare9(_reader: &mut ArgumentReader<'_>) -> Result<Arc<dyn PreparedValidator>, BindError> {
99    Ok(Arc::new(Accept { dependencies: D9 }))
100}
101
102static S0: &[ValidatorSignature] = &[ValidatorSignature::new(InputType::Text, D0, prepare0)];
103static S1: &[ValidatorSignature] = &[ValidatorSignature::new(InputType::Text, D1, prepare1)];
104static S4: &[ValidatorSignature] = &[ValidatorSignature::new(InputType::Text, D4, prepare4)];
105static S8: &[ValidatorSignature] = &[ValidatorSignature::new(InputType::Text, D8, prepare8)];
106static S9: &[ValidatorSignature] = &[ValidatorSignature::new(InputType::Text, D9, prepare9)];
107static V0: ValidatorDescriptor = ValidatorDescriptor::new(S0);
108static V1: ValidatorDescriptor = ValidatorDescriptor::new(S1);
109static V4: ValidatorDescriptor = ValidatorDescriptor::new(S4);
110static V8: ValidatorDescriptor = ValidatorDescriptor::new(S8);
111static V9: ValidatorDescriptor = ValidatorDescriptor::new(S9);
112
113fn ns_per_call(iterations: u64, mut call: impl FnMut()) -> f64 {
114    let start = Instant::now();
115    for _ in 0..iterations {
116        call();
117    }
118    start.elapsed().as_nanos() as f64 / iterations as f64
119}
120
121fn median(samples: &[f64]) -> f64 {
122    let mut sorted = samples.to_vec();
123    sorted.sort_by(f64::total_cmp);
124    sorted[samples.len() / 2]
125}
126
127fn run_case(
128    label: &str,
129    bound: &BoundValidator,
130    named: &[NamedValidationDependency<'_>],
131    ordered: &BoundValidationContext<'_>,
132) {
133    let value = ValidationValue::Text("sample");
134    assert!(
135        matches!(bound.validate_named(value, named), Ok(ValidationOutcome::Valid)),
136        "named benchmark setup must validate"
137    );
138    assert!(
139        matches!(bound.validate(value, ordered), Ok(ValidationOutcome::Valid)),
140        "ordered benchmark setup must validate"
141    );
142    let mut call_named = || {
143        let _ = black_box(black_box(bound).validate_named(black_box(value), black_box(named)));
144    };
145    let mut call_ordered = || {
146        let _ = black_box(black_box(bound).validate(black_box(value), black_box(ordered)));
147    };
148    ns_per_call(100_000, &mut call_named);
149    ns_per_call(100_000, &mut call_ordered);
150    let mut named_samples = Vec::new();
151    let mut ordered_samples = Vec::new();
152    for round in 0..5 {
153        if round % 2 == 0 {
154            named_samples.push(ns_per_call(1_000_000, &mut call_named));
155            ordered_samples.push(ns_per_call(1_000_000, &mut call_ordered));
156        } else {
157            ordered_samples.push(ns_per_call(1_000_000, &mut call_ordered));
158            named_samples.push(ns_per_call(1_000_000, &mut call_named));
159        }
160    }
161    println!(
162        "{label}: named={named_samples:?} ordered={ordered_samples:?} median_named={} median_ordered={}",
163        median(&named_samples),
164        median(&ordered_samples),
165    );
166}
167
168fn main() {
169    let b0 = V0.bind(ValidatorId::new("bench.zero"), 0, &[]).unwrap();
170    let b1 = V1.bind(ValidatorId::new("bench.one"), 0, &[]).unwrap();
171    let b4 = V4.bind(ValidatorId::new("bench.four"), 0, &[]).unwrap();
172    let b8 = V8.bind(ValidatorId::new("bench.eight"), 0, &[]).unwrap();
173    let b9 = V9.bind(ValidatorId::new("bench.nine"), 0, &[]).unwrap();
174    let number = 7_u64;
175    let value = ValidationValue::Typed(&number);
176    let path = ValidationPath::root().with_field("sample");
177    let tail_path = ValidationPath::root().with_field("tail");
178
179    let empty_named: [NamedValidationDependency<'_>; 0] = [];
180    let empty_values: [ValidationValue<'_>; 0] = [];
181    run_case("zero", &b0, &empty_named, &BoundValidationContext::new(&empty_values));
182
183    let one_named = [NamedValidationDependency::new("a", value)];
184    let one_values = [value];
185    run_case("one_plain", &b1, &one_named, &BoundValidationContext::new(&one_values));
186    let one_named_path = [NamedValidationDependency::new("a", value).with_path(&path)];
187    let one_paths = [path.clone()];
188    let one_context = BoundValidationContext::new_with_paths(&one_values, &one_paths).unwrap();
189    run_case("one_path", &b1, &one_named_path, &one_context);
190
191    let four_named = [
192        NamedValidationDependency::new("a", value),
193        NamedValidationDependency::new("b", value),
194        NamedValidationDependency::new("c", value),
195        NamedValidationDependency::new("d", value),
196    ];
197    let four_values = [value; 4];
198    run_case(
199        "four_plain",
200        &b4,
201        &four_named,
202        &BoundValidationContext::new(&four_values),
203    );
204    let four_named_path = [
205        NamedValidationDependency::new("a", value).with_path(&path),
206        NamedValidationDependency::new("b", value).with_path(&path),
207        NamedValidationDependency::new("c", value).with_path(&path),
208        NamedValidationDependency::new("d", value).with_path(&path),
209    ];
210    let four_paths = [path.clone(), path.clone(), path.clone(), path.clone()];
211    let four_context = BoundValidationContext::new_with_paths(&four_values, &four_paths).unwrap();
212    run_case("four_path", &b4, &four_named_path, &four_context);
213
214    let eight_named = [
215        NamedValidationDependency::new("h", value),
216        NamedValidationDependency::new("g", value),
217        NamedValidationDependency::new("f", value),
218        NamedValidationDependency::new("e", value),
219        NamedValidationDependency::new("d", value),
220        NamedValidationDependency::new("c", value),
221        NamedValidationDependency::new("b", value),
222        NamedValidationDependency::new("a", value),
223    ];
224    let eight_values = [value; 8];
225    let eight_context = BoundValidationContext::new(&eight_values);
226    run_case("eight_plain", &b8, &eight_named, &eight_context);
227    let eight_named_mixed = [
228        NamedValidationDependency::new("h", value).with_path(&path),
229        NamedValidationDependency::new("g", value),
230        NamedValidationDependency::new("f", value),
231        NamedValidationDependency::new("e", value),
232        NamedValidationDependency::new("d", value),
233        NamedValidationDependency::new("c", value),
234        NamedValidationDependency::new("b", value),
235        NamedValidationDependency::new("a", value).with_path(&tail_path),
236    ];
237    let eight_paths = [
238        tail_path.clone(),
239        ValidationPath::root(),
240        ValidationPath::root(),
241        ValidationPath::root(),
242        ValidationPath::root(),
243        ValidationPath::root(),
244        ValidationPath::root(),
245        path.clone(),
246    ];
247    let eight_context = BoundValidationContext::new_with_paths(&eight_values, &eight_paths).unwrap();
248    run_case("eight_mixed_path", &b8, &eight_named_mixed, &eight_context);
249
250    let nine_named = [
251        NamedValidationDependency::new("i", value),
252        NamedValidationDependency::new("h", value),
253        NamedValidationDependency::new("g", value),
254        NamedValidationDependency::new("f", value),
255        NamedValidationDependency::new("e", value),
256        NamedValidationDependency::new("d", value),
257        NamedValidationDependency::new("c", value),
258        NamedValidationDependency::new("b", value),
259        NamedValidationDependency::new("a", value),
260    ];
261    let nine_values = [value; 9];
262    let nine_context = BoundValidationContext::new(&nine_values);
263    run_case("nine_plain", &b9, &nine_named, &nine_context);
264    let nine_named_mixed = [
265        NamedValidationDependency::new("i", value).with_path(&path),
266        NamedValidationDependency::new("h", value),
267        NamedValidationDependency::new("g", value),
268        NamedValidationDependency::new("f", value),
269        NamedValidationDependency::new("e", value),
270        NamedValidationDependency::new("d", value),
271        NamedValidationDependency::new("c", value),
272        NamedValidationDependency::new("b", value),
273        NamedValidationDependency::new("a", value).with_path(&tail_path),
274    ];
275    let nine_paths = [
276        tail_path.clone(),
277        ValidationPath::root(),
278        ValidationPath::root(),
279        ValidationPath::root(),
280        ValidationPath::root(),
281        ValidationPath::root(),
282        ValidationPath::root(),
283        ValidationPath::root(),
284        path.clone(),
285    ];
286    let nine_context = BoundValidationContext::new_with_paths(&nine_values, &nine_paths).unwrap();
287    run_case("nine_mixed_path", &b9, &nine_named_mixed, &nine_context);
288}