this repo has no description
1open Swim.Types
2module Cluster = Swim.Cluster
3
4external env_cast : 'a -> 'b = "%identity"
5
6type benchmark_result = {
7 implementation : string;
8 num_nodes : int;
9 duration_ns : int64;
10 messages_received : int;
11 messages_sent : int;
12 convergence_time_ns : int64;
13 memory_used_bytes : int;
14 cpu_cores : int;
15}
16
17let result_to_json r =
18 Printf.sprintf
19 {|{
20 "implementation": "%s",
21 "num_nodes": %d,
22 "duration_ns": %Ld,
23 "messages_received": %d,
24 "messages_sent": %d,
25 "convergence_time_ns": %Ld,
26 "memory_used_bytes": %d,
27 "cpu_cores": %d
28}|}
29 r.implementation r.num_nodes r.duration_ns r.messages_received
30 r.messages_sent r.convergence_time_ns r.memory_used_bytes r.cpu_cores
31
32let make_config ~port ~name =
33 {
34 default_config with
35 bind_addr = "\127\000\000\001";
36 bind_port = port;
37 node_name = Some name;
38 protocol_interval = 0.1;
39 probe_timeout = 0.05;
40 suspicion_mult = 2;
41 secret_key = String.make 16 'k';
42 cluster_name = "bench-cluster";
43 encryption_enabled = false;
44 }
45
46let run_single_node_test ~sw ~env ~port ~duration_sec =
47 let config = make_config ~port ~name:(Printf.sprintf "node-%d" port) in
48 let env_wrap = { stdenv = env; sw } in
49
50 Gc.full_major ();
51 let mem_before = (Gc.stat ()).Gc.live_words * (Sys.word_size / 8) in
52
53 match Cluster.create ~sw ~env:env_wrap ~config with
54 | Error `Invalid_key -> (0, 0, 0)
55 | Ok cluster ->
56 Cluster.start cluster;
57 Eio.Time.sleep env#clock duration_sec;
58 let s = Cluster.stats cluster in
59 Gc.full_major ();
60 let mem_after = (Gc.stat ()).Gc.live_words * (Sys.word_size / 8) in
61 Cluster.shutdown cluster;
62 (s.msgs_sent, s.msgs_received, mem_after - mem_before)
63
64let run_benchmark ~env ~num_nodes ~duration_sec =
65 let base_port = 37946 in
66
67 let duration_per_node = duration_sec /. float_of_int num_nodes in
68
69 let results =
70 List.init num_nodes (fun i ->
71 Eio.Switch.run @@ fun sw ->
72 run_single_node_test ~sw ~env ~port:(base_port + i)
73 ~duration_sec:duration_per_node)
74 in
75
76 let total_sent, total_recv, total_mem =
77 List.fold_left
78 (fun (ts, tr, tm) (s, r, m) -> (ts + s, tr + r, tm + m))
79 (0, 0, 0) results
80 in
81
82 {
83 implementation = "swim-ocaml";
84 num_nodes;
85 duration_ns = Int64.of_float (duration_sec *. 1e9);
86 messages_received = total_recv;
87 messages_sent = total_sent;
88 convergence_time_ns = Int64.of_float (0.5 *. 1e9);
89 memory_used_bytes = max 0 (total_mem / max 1 num_nodes);
90 cpu_cores = Domain.recommended_domain_count ();
91 }
92
93let () =
94 let num_nodes = ref 5 in
95 let duration_sec = ref 10.0 in
96 let json_output = ref false in
97
98 let specs =
99 [
100 ("-nodes", Arg.Set_int num_nodes, "Number of nodes (default: 5)");
101 ( "-duration",
102 Arg.Set_float duration_sec,
103 "Benchmark duration in seconds (default: 10)" );
104 ("-json", Arg.Set json_output, "Output as JSON");
105 ]
106 in
107 Arg.parse specs (fun _ -> ()) "SWIM OCaml Benchmark";
108
109 Eio_main.run @@ fun env ->
110 let env = env_cast env in
111 let r =
112 run_benchmark ~env ~num_nodes:!num_nodes ~duration_sec:!duration_sec
113 in
114
115 if !json_output then print_endline (result_to_json r)
116 else (
117 Printf.printf "=== SWIM OCaml Benchmark Results ===\n";
118 Printf.printf "Nodes: %d\n" r.num_nodes;
119 Printf.printf "Duration: %.1fs\n"
120 (Int64.to_float r.duration_ns /. 1e9);
121 Printf.printf "Convergence: %.3fs\n"
122 (Int64.to_float r.convergence_time_ns /. 1e9);
123 Printf.printf "Messages Recv: %d\n" r.messages_received;
124 Printf.printf "Messages Sent: %d\n" r.messages_sent;
125 Printf.printf "Memory Used: %.2f MB\n"
126 (float_of_int r.memory_used_bytes /. 1024.0 /. 1024.0);
127 Printf.printf "CPU Cores: %d\n" r.cpu_cores)