open Swim.Types module Cluster = Swim.Cluster external env_cast : 'a -> 'b = "%identity" type benchmark_result = { implementation : string; num_nodes : int; duration_ns : int64; messages_received : int; messages_sent : int; convergence_time_ns : int64; memory_used_bytes : int; cpu_cores : int; } let result_to_json r = Printf.sprintf {|{ "implementation": "%s", "num_nodes": %d, "duration_ns": %Ld, "messages_received": %d, "messages_sent": %d, "convergence_time_ns": %Ld, "memory_used_bytes": %d, "cpu_cores": %d }|} r.implementation r.num_nodes r.duration_ns r.messages_received r.messages_sent r.convergence_time_ns r.memory_used_bytes r.cpu_cores let make_config ~port ~name = { default_config with bind_addr = "\127\000\000\001"; bind_port = port; node_name = Some name; protocol_interval = 0.1; probe_timeout = 0.05; suspicion_mult = 2; secret_key = String.make 16 'k'; cluster_name = "bench-cluster"; encryption_enabled = false; } let run_single_node_test ~sw ~env ~port ~duration_sec = let config = make_config ~port ~name:(Printf.sprintf "node-%d" port) in let env_wrap = { stdenv = env; sw } in Gc.full_major (); let mem_before = (Gc.stat ()).Gc.live_words * (Sys.word_size / 8) in match Cluster.create ~sw ~env:env_wrap ~config with | Error `Invalid_key -> (0, 0, 0) | Ok cluster -> Cluster.start cluster; Eio.Time.sleep env#clock duration_sec; let s = Cluster.stats cluster in Gc.full_major (); let mem_after = (Gc.stat ()).Gc.live_words * (Sys.word_size / 8) in Cluster.shutdown cluster; (s.msgs_sent, s.msgs_received, mem_after - mem_before) let run_benchmark ~env ~num_nodes ~duration_sec = let base_port = 37946 in let duration_per_node = duration_sec /. float_of_int num_nodes in let results = List.init num_nodes (fun i -> Eio.Switch.run @@ fun sw -> run_single_node_test ~sw ~env ~port:(base_port + i) ~duration_sec:duration_per_node) in let total_sent, total_recv, total_mem = List.fold_left (fun (ts, tr, tm) (s, r, m) -> (ts + s, tr + r, tm + m)) (0, 0, 0) results in { implementation = "swim-ocaml"; num_nodes; duration_ns = Int64.of_float (duration_sec *. 1e9); messages_received = total_recv; messages_sent = total_sent; convergence_time_ns = Int64.of_float (0.5 *. 1e9); memory_used_bytes = max 0 (total_mem / max 1 num_nodes); cpu_cores = Domain.recommended_domain_count (); } let () = let num_nodes = ref 5 in let duration_sec = ref 10.0 in let json_output = ref false in let specs = [ ("-nodes", Arg.Set_int num_nodes, "Number of nodes (default: 5)"); ( "-duration", Arg.Set_float duration_sec, "Benchmark duration in seconds (default: 10)" ); ("-json", Arg.Set json_output, "Output as JSON"); ] in Arg.parse specs (fun _ -> ()) "SWIM OCaml Benchmark"; Eio_main.run @@ fun env -> let env = env_cast env in let r = run_benchmark ~env ~num_nodes:!num_nodes ~duration_sec:!duration_sec in if !json_output then print_endline (result_to_json r) else ( Printf.printf "=== SWIM OCaml Benchmark Results ===\n"; Printf.printf "Nodes: %d\n" r.num_nodes; Printf.printf "Duration: %.1fs\n" (Int64.to_float r.duration_ns /. 1e9); Printf.printf "Convergence: %.3fs\n" (Int64.to_float r.convergence_time_ns /. 1e9); Printf.printf "Messages Recv: %d\n" r.messages_received; Printf.printf "Messages Sent: %d\n" r.messages_sent; Printf.printf "Memory Used: %.2f MB\n" (float_of_int r.memory_used_bytes /. 1024.0 /. 1024.0); Printf.printf "CPU Cores: %d\n" r.cpu_cores)