// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use super::Result; use std::{collections::HashMap, path::Path, process::Command, time::Duration}; pub use test_util::{parse_wrk_output, WrkOutput as HttpBenchmarkResult}; // Some of the benchmarks in this file have been renamed. In case the history // somehow gets messed up: // "node_http" was once called "node" // "deno_tcp" was once called "deno" // "deno_http" was once called "deno_net_http" const DURATION: &str = "20s"; pub(crate) fn benchmark( target_path: &Path, ) -> Result> { let deno_exe = test_util::deno_exe_path(); let deno_exe = deno_exe.to_str().unwrap(); let hyper_hello_exe = target_path.join("test_server"); let hyper_hello_exe = hyper_hello_exe.to_str().unwrap(); let core_http_json_ops_exe = target_path.join("examples/http_bench_json_ops"); let core_http_json_ops_exe = core_http_json_ops_exe.to_str().unwrap(); let mut res = HashMap::new(); // "deno_tcp" was once called "deno" res.insert("deno_tcp".to_string(), deno_tcp(deno_exe)?); // res.insert("deno_udp".to_string(), deno_udp(deno_exe)?); res.insert("deno_http".to_string(), deno_http(deno_exe)?); res.insert("deno_http_native".to_string(), deno_http_native(deno_exe)?); // TODO(ry) deno_proxy disabled to make fetch() standards compliant. // res.insert("deno_proxy".to_string(), deno_http_proxy(deno_exe) hyper_hello_exe)) res.insert( "deno_proxy_tcp".to_string(), deno_tcp_proxy(deno_exe, hyper_hello_exe)?, ); // "core_http_json_ops" previously had a "bin op" counterpart called "core_http_bin_ops", // which was previously also called "deno_core_http_bench", "deno_core_single" res.insert( "core_http_json_ops".to_string(), core_http_json_ops(core_http_json_ops_exe)?, ); // "node_http" was once called "node" res.insert("node_http".to_string(), node_http()?); res.insert("node_proxy".to_string(), node_http_proxy(hyper_hello_exe)?); res.insert( "node_proxy_tcp".to_string(), node_tcp_proxy(hyper_hello_exe)?, ); res.insert("node_tcp".to_string(), node_tcp()?); res.insert("hyper".to_string(), hyper_http(hyper_hello_exe)?); Ok(res) } fn run( server_cmd: &[&str], port: u16, env: Option>, origin_cmd: Option<&[&str]>, ) -> Result { // Wait for port 4544 to become available. // TODO Need to use SO_REUSEPORT with tokio::net::TcpListener. std::thread::sleep(Duration::from_secs(5)); let mut origin = None; if let Some(cmd) = origin_cmd { let mut com = Command::new(cmd[0]); com.args(&cmd[1..]); if let Some(env) = env.clone() { com.envs(env); } origin = Some(com.spawn()?); }; println!("{}", server_cmd.join(" ")); let mut server = { let mut com = Command::new(server_cmd[0]); com.args(&server_cmd[1..]); if let Some(env) = env { com.envs(env); } com.spawn()? }; std::thread::sleep(Duration::from_secs(5)); // wait for server to wake up. TODO racy. let wrk = test_util::prebuilt_tool_path("wrk"); assert!(wrk.is_file()); let wrk_cmd = &[ wrk.to_str().unwrap(), "-d", DURATION, "--latency", &format!("http://127.0.0.1:{}/", port), ]; println!("{}", wrk_cmd.join(" ")); let output = test_util::run_collect(wrk_cmd, None, None, None, true).0; std::thread::sleep(Duration::from_secs(1)); // wait to capture failure. TODO racy. println!("{}", output); assert!( server.try_wait()?.map_or(true, |s| s.success()), "server ended with error" ); server.kill()?; if let Some(mut origin) = origin { origin.kill()?; } Ok(parse_wrk_output(&output)) } fn get_port() -> u16 { static mut NEXT_PORT: u16 = 4544; let port = unsafe { NEXT_PORT }; unsafe { NEXT_PORT += 1; } port } fn server_addr(port: u16) -> String { format!("0.0.0.0:{}", port) } fn deno_tcp(deno_exe: &str) -> Result { let port = get_port(); println!("http_benchmark testing DENO tcp."); run( &[ deno_exe, "run", "--allow-net", "cli/bench/deno_tcp.ts", &server_addr(port), ], port, None, None, ) } fn deno_tcp_proxy( deno_exe: &str, hyper_exe: &str, ) -> Result { let port = get_port(); let origin_port = get_port(); println!("http_proxy_benchmark testing DENO using net/tcp."); run( &[ deno_exe, "run", "--allow-net", "--reload", "--unstable", "cli/bench/deno_tcp_proxy.ts", &server_addr(port), &server_addr(origin_port), ], port, None, Some(&[hyper_exe, &origin_port.to_string()]), ) } fn deno_http(deno_exe: &str) -> Result { let port = get_port(); println!("http_benchmark testing DENO using net/http."); run( &[ deno_exe, "run", "--allow-net", "--reload", "--unstable", "test_util/std/http/bench.ts", &server_addr(port), ], port, None, None, ) } fn deno_http_native(deno_exe: &str) -> Result { let port = get_port(); println!("http_benchmark testing DENO using native bindings."); run( &[ deno_exe, "run", "--allow-net", "--reload", "cli/bench/deno_http_native.js", &server_addr(port), ], port, None, None, ) } #[allow(dead_code)] fn deno_http_proxy( deno_exe: &str, hyper_exe: &str, ) -> Result { let port = get_port(); let origin_port = get_port(); println!("http_proxy_benchmark testing DENO using net/http."); run( &[ deno_exe, "run", "--allow-net", "--reload", "--unstable", "cli/bench/deno_http_proxy.ts", &server_addr(port), &server_addr(origin_port), ], port, None, Some(&[hyper_exe, &origin_port.to_string()]), ) } fn core_http_json_ops(exe: &str) -> Result { println!("http_benchmark testing CORE http_bench_json_ops"); run(&[exe], 4544, None, None) } fn node_http() -> Result { let port = get_port(); println!("http_benchmark testing NODE."); run( &["node", "cli/bench/node_http.js", &port.to_string()], port, None, None, ) } fn node_http_proxy(hyper_exe: &str) -> Result { let port = get_port(); let origin_port = get_port(); let origin_port = origin_port.to_string(); println!("http_proxy_benchmark testing NODE."); run( &[ "node", "cli/bench/node_http_proxy.js", &port.to_string(), &origin_port, ], port, None, Some(&[hyper_exe, &origin_port]), ) } fn node_tcp_proxy(exe: &str) -> Result { let port = get_port(); let origin_port = get_port(); let origin_port = origin_port.to_string(); println!("http_proxy_benchmark testing NODE tcp."); run( &[ "node", "cli/bench/node_tcp_proxy.js", &port.to_string(), &origin_port, ], port, None, Some(&[exe, &origin_port]), ) } fn node_tcp() -> Result { let port = get_port(); println!("http_benchmark testing node_tcp.js"); run( &["node", "cli/bench/node_tcp.js", &port.to_string()], port, None, None, ) } fn hyper_http(exe: &str) -> Result { let port = get_port(); println!("http_benchmark testing RUST hyper"); run(&[exe, &port.to_string()], port, None, None) }