Skip to content

Commit 1dcbc15

Browse files
authored
Merge master into feature/config-ntp-timezone-maxcstate (#6777)
2 parents 02ee3af + 1851370 commit 1dcbc15

File tree

13 files changed

+328
-43
lines changed

13 files changed

+328
-43
lines changed

dune-project

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@
177177
(xapi-idl
178178
(= :version))
179179
(xapi-types
180-
(= :version)))
180+
(= :version))
181+
(xapi-stdext-zerocheck
182+
(= :version)))
181183
(synopsis "A CLI for xapi storage services")
182184
(description
183185
"The CLI allows you to directly manipulate virtual disk images, without them being attached to VMs."))
@@ -322,6 +324,7 @@
322324
xapi-types
323325
xapi-stdext-pervasives
324326
xapi-stdext-unix
327+
xapi-stdext-zerocheck
325328
xen-api-client
326329
xen-api-client-lwt
327330
xenctrl
@@ -874,4 +877,5 @@
874877
(synopsis "Xapi's standard library extension, Zerocheck")
875878
(authors "Jonathan Ludlam")
876879
(depends
880+
(alcotest :with-test)
877881
(odoc :with-doc)))
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
open Bechamel
2+
open Toolkit
3+
module XString = Xapi_stdext_std.Xstringext.String
4+
5+
(* Test data generators *)
6+
let make_string len = String.init len (fun i -> Char.chr (33 + (i mod 94)))
7+
8+
let escape_rules =
9+
[('a', "[A]"); ('e', "[E]"); ('i', "[I]"); ('o', "[O]"); ('u', "[U]")]
10+
11+
(* Reference implementation from xstringext_test.ml *)
12+
let escaped_spec ?rules string =
13+
match rules with
14+
| None ->
15+
String.escaped string
16+
| Some rules ->
17+
let apply_rules char =
18+
match List.assoc_opt char rules with
19+
| None ->
20+
Seq.return char
21+
| Some replacement ->
22+
String.to_seq replacement
23+
in
24+
string |> String.to_seq |> Seq.concat_map apply_rules |> String.of_seq
25+
26+
let escaped_benchmark n =
27+
let s = make_string n in
28+
Staged.stage @@ fun () -> ignore (XString.escaped ~rules:escape_rules s)
29+
30+
let escaped_spec_benchmark n =
31+
let s = make_string n in
32+
Staged.stage @@ fun () -> ignore (escaped_spec ~rules:escape_rules s)
33+
34+
let test_escaped =
35+
Test.make_indexed ~name:"escaped" ~fmt:"%s %d" ~args:[100; 500; 1000]
36+
escaped_benchmark
37+
38+
let test_escaped_spec =
39+
Test.make_indexed ~name:"escaped-spec" ~fmt:"%s %d" ~args:[100; 500; 1000]
40+
escaped_spec_benchmark
41+
42+
let benchmark () =
43+
let ols =
44+
Analyze.ols ~bootstrap:0 ~r_square:true ~predictors:Measure.[|run|]
45+
in
46+
let instances =
47+
Instance.[minor_allocated; major_allocated; monotonic_clock]
48+
in
49+
let cfg =
50+
Benchmark.cfg ~limit:2000 ~quota:(Time.second 0.5) ~kde:(Some 1000) ()
51+
in
52+
let test =
53+
Test.make_grouped ~name:"escaped-comparison"
54+
[test_escaped; test_escaped_spec]
55+
in
56+
let raw_results = Benchmark.all cfg instances test in
57+
let results =
58+
List.map (fun instance -> Analyze.all ols instance raw_results) instances
59+
in
60+
let results = Analyze.merge ols instances results in
61+
(results, raw_results)
62+
63+
let () =
64+
let all_results = benchmark () in
65+
let results, _ = all_results in
66+
67+
(* Extract timing data from the actual benchmark results *)
68+
let result_groups =
69+
Hashtbl.fold
70+
(fun _ v a -> Hashtbl.fold (fun k v a -> (k, v) :: a) v [] :: a)
71+
results []
72+
in
73+
74+
(* Find the monotonic-clock result group (timing data) *)
75+
let timing_group =
76+
match result_groups with _ :: _ :: timing :: _ -> Some timing | _ -> None
77+
in
78+
79+
let get_timing test_name =
80+
match timing_group with
81+
| None ->
82+
None
83+
| Some group -> (
84+
match List.assoc_opt test_name group with
85+
| Some estimator -> (
86+
let estimates = Analyze.OLS.estimates estimator in
87+
match estimates with Some (x :: _) -> Some x | _ -> None
88+
)
89+
| None ->
90+
None
91+
)
92+
in
93+
94+
Printf.printf "\n=== Performance Comparison: Optimized vs Reference ===\n\n" ;
95+
96+
let sizes = ["100"; "500"; "1000"] in
97+
List.iter
98+
(fun size ->
99+
Printf.printf "String size %s:\n" size ;
100+
let opt_test = Printf.sprintf "escaped-comparison/escaped %s" size in
101+
let ref_test = Printf.sprintf "escaped-comparison/escaped-spec %s" size in
102+
match (get_timing opt_test, get_timing ref_test) with
103+
| Some opt_time, Some ref_time ->
104+
let improvement = (ref_time -. opt_time) /. ref_time *. 100.0 in
105+
Printf.printf " Optimized: %.3f μs\n" opt_time ;
106+
Printf.printf " Reference: %.3f μs\n" ref_time ;
107+
Printf.printf " Improvement: %.1f%% %s\n\n" improvement
108+
(if improvement > 0.0 then "faster" else "slower")
109+
| None, _ ->
110+
Printf.printf " Optimized implementation data missing\n\n"
111+
| _, None ->
112+
Printf.printf " Reference implementation data missing\n\n"
113+
)
114+
sizes ;
115+
116+
Printf.printf "\n=== Detailed Results ===\n" ;
117+
match result_groups with
118+
| [results] ->
119+
let print (k, ols) = Fmt.pr "%s: %a\n%!" k Analyze.OLS.pp ols in
120+
List.iter print results
121+
| results_list ->
122+
Printf.printf "Results structure: %d result groups\n"
123+
(List.length results_list) ;
124+
List.iteri
125+
(fun i results ->
126+
Printf.printf "Result group %d:\n" i ;
127+
let print (k, ols) = Fmt.pr " %s: %a\n%!" k Analyze.OLS.pp ols in
128+
List.iter print results
129+
)
130+
results_list

ocaml/libs/xapi-stdext/lib/xapi-stdext-std/bench/bench_xstringext.mli

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(executable
2+
(name bench_xstringext)
3+
(modes exe)
4+
(optional)
5+
(libraries bechamel xapi-stdext-std bechamel-notty notty.unix fmt)
6+
)

ocaml/libs/xapi-stdext/lib/xapi-stdext-std/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
(names xstringext_test listext_test)
88
(package xapi-stdext-std)
99
(modules xstringext_test listext_test)
10-
(libraries xapi_stdext_std fmt alcotest)
10+
(libraries xapi_stdext_std fmt alcotest qcheck-core qcheck-alcotest)
1111
)

ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext.ml

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,6 @@ module String = struct
4242
(** Returns true for whitespace characters, false otherwise *)
4343
let isspace = function ' ' | '\n' | '\r' | '\t' -> true | _ -> false
4444

45-
let escaped ?rules string =
46-
match rules with
47-
| None ->
48-
String.escaped string
49-
| Some rules ->
50-
let aux h t =
51-
( if List.mem_assoc h rules then
52-
List.assoc h rules
53-
else
54-
of_char h
55-
)
56-
:: t
57-
in
58-
concat "" (fold_right aux string [])
59-
6045
let split_f p str =
6146
let split_one seq =
6247
let not_p c = not (p c) in
@@ -193,6 +178,13 @@ module String = struct
193178
) else
194179
s
195180

181+
let escaped ?rules s =
182+
match rules with
183+
| None ->
184+
String.escaped s
185+
| Some rules ->
186+
map_unlikely s (fun c -> List.assoc_opt c rules)
187+
196188
let sub_to_end s start =
197189
let length = String.length s in
198190
String.sub s start (length - start)

ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext_test.ml

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,49 @@ let test_rtrim =
147147
in
148148
("rtrim", List.map test spec)
149149

150+
(** Simple implementation of escaped for testing against *)
151+
let escaped_spec ?rules string =
152+
match rules with
153+
| None ->
154+
String.escaped string
155+
| Some rules ->
156+
let apply_rules char =
157+
match List.assoc_opt char rules with
158+
| None ->
159+
Seq.return char
160+
| Some replacement ->
161+
String.to_seq replacement
162+
in
163+
string |> String.to_seq |> Seq.concat_map apply_rules |> String.of_seq
164+
165+
let test_escaped =
166+
let open QCheck2 in
167+
(* Generator for escape rules: list of (char, string) mappings *)
168+
let gen_rules =
169+
let open Gen in
170+
let gen_rule = pair char (string_size (int_range 0 5) ~gen:char) in
171+
list gen_rule
172+
in
173+
(* Generator for test input: string and optional rules *)
174+
let gen_input = Gen.pair Gen.string (Gen.opt gen_rules) in
175+
let property (s, rules) =
176+
let expected = escaped_spec ?rules s in
177+
let actual = XString.escaped ?rules s in
178+
String.equal expected actual
179+
in
180+
let test =
181+
Test.make ~name:"escaped matches reference implementation" ~count:1000
182+
gen_input property
183+
in
184+
("escaped", [QCheck_alcotest.to_alcotest test])
185+
150186
let () =
151187
Alcotest.run "Xstringext"
152-
[test_rev_map; test_split; test_split_f; test_has_substr; test_rtrim]
188+
[
189+
test_rev_map
190+
; test_split
191+
; test_split_f
192+
; test_has_substr
193+
; test_rtrim
194+
; test_escaped
195+
]

ocaml/libs/xapi-stdext/lib/xapi-stdext-std/xstringext_test.mli

Whitespace-only changes.

0 commit comments

Comments
 (0)