-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a network benchmark using an HTTP-like protocol
This runs 4 client domains and 4 server domains. Each client opens 25 connections and makes 1000 HTTP POSTs on each one. For each request it uses Buf_write to write the headers and body, then Buf_read to parse the response. Likewise, the server uses Buf_read and Buf_write to handle the each request. On my system, I get this using eio_linux (eio_posix is about the same): clients, servers, requests, requests/s 4, 4, 100000, 367051.7
- Loading branch information
Showing
3 changed files
with
106 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
(* A multi-domain server handles HTTP-like requests from many clients running across multiple domains. *) | ||
|
||
open Eio.Std | ||
|
||
let n_client_domains = 4 | ||
let n_server_domains = 4 | ||
let n_connections_per_domain = 25 | ||
let n_requests_per_connection = 1000 | ||
|
||
(* Note: this is not a real HTTP parser! *) | ||
let key_char = function | ||
| 'A'..'Z' | 'a'..'z' | '-' -> true | ||
| _ -> false | ||
|
||
let parse_headers r = | ||
let len = ref (-1) in | ||
let rec aux () = | ||
let key = Eio.Buf_read.take_while key_char r in | ||
if key = "" then Eio.Buf_read.string "\r\n" r | ||
else ( | ||
Eio.Buf_read.char ':' r; | ||
let value = Eio.Buf_read.line r in | ||
if key = "Content-Length" then len := int_of_string (String.trim value); | ||
aux () | ||
) | ||
in | ||
aux (); | ||
!len | ||
|
||
let handle_connection conn _addr = | ||
Eio.Buf_write.with_flow conn @@ fun w -> | ||
let rec requests r = | ||
let _req = Eio.Buf_read.line r in | ||
let len = parse_headers r in | ||
let body = Eio.Buf_read.take len r in | ||
let response = body ^ " / received" in | ||
Eio.Buf_write.string w "HTTP/1.1 200 OK\r\n"; | ||
Eio.Buf_write.string w (Printf.sprintf "Content-Length: %d\r\n" (String.length response)); | ||
Eio.Buf_write.string w "\r\n"; | ||
Eio.Buf_write.string w response; | ||
if not (Eio.Buf_read.at_end_of_input r) then requests r | ||
in | ||
Eio.Buf_read.parse_exn requests conn ~max_size:max_int | ||
|
||
let run_client id conn = | ||
let total = ref 0 in | ||
let r = Eio.Buf_read.of_flow conn ~max_size:max_int in | ||
Eio.Buf_write.with_flow conn @@ fun w -> | ||
for i = 1 to n_requests_per_connection do | ||
let msg = Printf.sprintf "%s / request %d" id i in | ||
Eio.Buf_write.string w "POST / HTTP/1.1\r\n"; | ||
Eio.Buf_write.string w "Host: localhost:8085\r\n"; | ||
Eio.Buf_write.string w "User-Agent: bench_server\r\n"; | ||
Eio.Buf_write.string w "Connection: keep-alive\r\n"; | ||
Eio.Buf_write.string w (Printf.sprintf "Content-Length: %d\r\n" (String.length msg)); | ||
Eio.Buf_write.string w "\r\n"; | ||
Eio.Buf_write.string w msg; | ||
let status = Eio.Buf_read.line r in | ||
assert (status = "HTTP/1.1 200 OK"); | ||
let len = parse_headers r in | ||
let body = Eio.Buf_read.take len r in | ||
assert (body = msg ^ " / received"); | ||
incr total | ||
done; | ||
!total | ||
|
||
let main net domain_mgr = | ||
let total = Atomic.make 0 in | ||
let t0 = Unix.gettimeofday () in | ||
Switch.run (fun sw -> | ||
let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 8085) in | ||
let server_socket = Eio.Net.listen ~reuse_addr:true ~backlog:100 ~sw net addr in | ||
Fiber.fork_daemon ~sw (fun () -> | ||
Eio.Net.run_server server_socket handle_connection | ||
~additional_domains:(domain_mgr, n_server_domains - 1) | ||
~on_error:raise | ||
); | ||
for domain = 1 to n_client_domains do | ||
Fiber.fork ~sw (fun () -> | ||
Eio.Domain_manager.run domain_mgr (fun () -> | ||
Switch.run @@ fun sw -> | ||
for i = 1 to n_connections_per_domain do | ||
Fiber.fork ~sw (fun () -> | ||
let id = Printf.sprintf "domain %d / conn %d" domain i in | ||
let conn = Eio.Net.connect ~sw net addr in | ||
let requests = run_client id conn in | ||
ignore (Atomic.fetch_and_add total requests : int) | ||
) | ||
done | ||
) | ||
) | ||
done | ||
); | ||
let t1 = Unix.gettimeofday () in | ||
Fmt.pr "clients, servers, requests, requests/s@."; | ||
let requests = n_connections_per_domain * n_client_domains * n_requests_per_connection in | ||
assert (requests = Atomic.get total); | ||
let req_per_s = float requests /. (t1 -. t0) in | ||
Fmt.pr "%7d, %7d, %8d, %.1f@." n_client_domains n_server_domains requests req_per_s | ||
|
||
let () = | ||
print_endline ""; (* work around dune bug *) | ||
Eio_main.run @@ fun env -> | ||
main env#net env#domain_mgr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
(executables | ||
(names bench_stream bench_promise bench_semaphore bench_yield bench_cancel bench_mutex | ||
bench_buf_read bench_condition bench_fd) | ||
bench_buf_read bench_condition bench_fd bench_server) | ||
(libraries eio_main)) |