1use 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}