summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Mackey <mrkurt@gmail.com>2019-06-06 21:46:18 -0500
committerRyan Dahl <ry@tinyclouds.org>2019-06-06 22:46:18 -0400
commit9a6cfd653d74ab23d9415e6ce67da22badb8101e (patch)
treedd1e32fb3947a957622c8cca491885f57623b67b
parent9bea576f3ea224ec72f371f6f0bc582171ca7890 (diff)
add tcp proxy benchmarks + split out website section for proxy req/s (#2464)
-rw-r--r--tools/deno_tcp_proxy.ts29
-rwxr-xr-xtools/http_benchmark.py26
-rw-r--r--tools/node_tcp_proxy.js68
-rw-r--r--website/app.js31
-rw-r--r--website/benchmarks.html130
5 files changed, 252 insertions, 32 deletions
diff --git a/tools/deno_tcp_proxy.ts b/tools/deno_tcp_proxy.ts
new file mode 100644
index 000000000..96877fa20
--- /dev/null
+++ b/tools/deno_tcp_proxy.ts
@@ -0,0 +1,29 @@
+// Used for benchmarking Deno's tcp proxy perfromance. See tools/http_benchmark.py
+const addr = Deno.args[1] || "127.0.0.1:4500";
+const originAddr = Deno.args[2] || "127.0.0.1:4501";
+
+const listener = Deno.listen("tcp", addr);
+
+async function handle(conn: Deno.Conn): Promise<void> {
+ const origin = await Deno.dial("tcp", originAddr);
+ try {
+ await Promise.all([Deno.copy(conn, origin), Deno.copy(origin, conn)]);
+ } catch (err) {
+ if (err.message !== "read error" && err.message !== "write error") {
+ throw err;
+ }
+ } finally {
+ conn.close();
+ origin.close();
+ }
+}
+
+async function main(): Promise<void> {
+ console.log("Listening on", addr);
+ while (true) {
+ const conn = await listener.accept();
+ handle(conn);
+ }
+}
+
+main();
diff --git a/tools/http_benchmark.py b/tools/http_benchmark.py
index 7348b6b5a..ca451a4e3 100755
--- a/tools/http_benchmark.py
+++ b/tools/http_benchmark.py
@@ -37,6 +37,18 @@ def deno_http(deno_exe):
})
+def deno_tcp_proxy(deno_exe, hyper_hello_exe):
+ deno_cmd = [
+ deno_exe, "run", "--allow-net", "tools/deno_tcp_proxy.ts", ADDR,
+ ORIGIN_ADDR
+ ]
+ print "http_proxy_benchmark testing DENO using net/tcp."
+ return run(
+ deno_cmd,
+ merge_env={"DENO_DIR": os.path.join(util.root_path, "js")},
+ origin_cmd=http_proxy_origin(hyper_hello_exe))
+
+
def deno_http_proxy(deno_exe, hyper_hello_exe):
deno_cmd = [
deno_exe, "run", "--allow-net", "tools/deno_http_proxy.ts", ADDR,
@@ -71,6 +83,16 @@ def node_http_proxy(hyper_hello_exe):
return run(node_cmd, None, http_proxy_origin(hyper_hello_exe))
+def node_tcp_proxy(hyper_hello_exe):
+ node_cmd = [
+ "node", "tools/node_tcp_proxy.js",
+ ADDR.split(":")[1],
+ ORIGIN_ADDR.split(":")[1]
+ ]
+ print "http_proxy_benchmark testing NODE tcp."
+ return run(node_cmd, None, http_proxy_origin(hyper_hello_exe))
+
+
def node_tcp():
node_cmd = ["node", "tools/node_tcp.js", ADDR.split(":")[1]]
print "http_benchmark testing node_tcp.js"
@@ -97,11 +119,13 @@ def http_benchmark(build_dir):
# "deno_http" was once called "deno_net_http"
"deno_http": deno_http(deno_exe),
"deno_proxy": deno_http_proxy(deno_exe, hyper_hello_exe),
+ "deno_proxy_tcp": deno_tcp_proxy(deno_exe, hyper_hello_exe),
"deno_core_single": deno_core_single(core_http_bench_exe),
"deno_core_multi": deno_core_multi(core_http_bench_exe),
# "node_http" was once called "node"
"node_http": node_http(),
"node_proxy": node_http_proxy(hyper_hello_exe),
+ "node_proxy_tcp": node_tcp_proxy(hyper_hello_exe),
"node_tcp": node_tcp(),
"hyper": hyper_http(hyper_hello_exe)
}
@@ -127,7 +151,7 @@ def run(server_cmd, merge_env=None, origin_cmd=None):
server = subprocess.Popen(server_cmd, env=env)
- time.sleep(5) # wait for server to wake up. TODO racy.
+ time.sleep(15) # wait for server to wake up. TODO racy.
try:
cmd = "third_party/wrk/%s/wrk -d %s http://%s/" % (util.platform(),
diff --git a/tools/node_tcp_proxy.js b/tools/node_tcp_proxy.js
new file mode 100644
index 000000000..7dc1b2612
--- /dev/null
+++ b/tools/node_tcp_proxy.js
@@ -0,0 +1,68 @@
+const net = require("net");
+
+process.on("uncaughtException", function(error) {
+ console.error(error);
+});
+
+if (process.argv.length != 4) {
+ console.log("usage: %s <localport> <remoteport>", process.argv[1]);
+ process.exit();
+}
+
+const localport = process.argv[2];
+const remoteport = process.argv[3];
+
+const remotehost = "127.0.0.1";
+
+const server = net.createServer(function(localsocket) {
+ const remotesocket = new net.Socket();
+
+ remotesocket.connect(remoteport, remotehost);
+
+ localsocket.on("data", function(data) {
+ const flushed = remotesocket.write(data);
+ if (!flushed) {
+ localsocket.pause();
+ }
+ });
+
+ remotesocket.on("data", function(data) {
+ const flushed = localsocket.write(data);
+ if (!flushed) {
+ remotesocket.pause();
+ }
+ });
+
+ localsocket.on("drain", function() {
+ remotesocket.resume();
+ });
+
+ remotesocket.on("drain", function() {
+ localsocket.resume();
+ });
+
+ localsocket.on("close", function() {
+ remotesocket.end();
+ });
+
+ remotesocket.on("close", function() {
+ localsocket.end();
+ });
+
+ localsocket.on("error", function() {
+ localsocket.end();
+ });
+
+ remotesocket.on("error", function() {
+ remotesocket.end();
+ });
+});
+
+server.listen(localport);
+
+console.log(
+ "redirecting connections from 127.0.0.1:%d to %s:%d",
+ localport,
+ remotehost,
+ remoteport
+);
diff --git a/website/app.js b/website/app.js
index 23618274b..9f16e8e45 100644
--- a/website/app.js
+++ b/website/app.js
@@ -42,6 +42,10 @@ export function createThroughputColumns(data) {
return createColumns(data, "throughput");
}
+export function createProxyColumns(data) {
+ return createColumns(data, "req_per_sec_proxy");
+}
+
export function createReqPerSecColumns(data) {
return createColumns(data, "req_per_sec");
}
@@ -197,15 +201,41 @@ export function drawCharts(dataUrl) {
return drawChartsFromBenchmarkData(dataUrl);
}
+const proxyFields = [
+ "req_per_sec"
+ //"max_latency"
+];
+function extractProxyFields(data) {
+ for (const row of data) {
+ for (const field of proxyFields) {
+ const d = row[field];
+ if (!d) continue;
+ const name = field + "_proxy";
+ const newField = {};
+ row[name] = newField;
+ for (const k of Object.getOwnPropertyNames(d)) {
+ if (k.includes("_proxy")) {
+ const v = d[k];
+ delete d[k];
+ newField[k] = v;
+ }
+ }
+ }
+ }
+}
/**
* Draws the charts from the benchmark data stored in gh-pages branch.
*/
export async function drawChartsFromBenchmarkData(dataUrl) {
const data = await getJson(dataUrl);
+ // hack to extract proxy fields from req/s fields
+ extractProxyFields(data);
+
const execTimeColumns = createExecTimeColumns(data);
const throughputColumns = createThroughputColumns(data);
const reqPerSecColumns = createReqPerSecColumns(data);
+ const proxyColumns = createProxyColumns(data);
const maxLatencyColumns = createMaxLatencyColumns(data);
const maxMemoryColumns = createMaxMemoryColumns(data);
const binarySizeColumns = createBinarySizeColumns(data);
@@ -235,6 +265,7 @@ export async function drawChartsFromBenchmarkData(dataUrl) {
gen("#exec-time-chart", execTimeColumns, "seconds", logScale);
gen("#throughput-chart", throughputColumns, "seconds", logScale);
gen("#req-per-sec-chart", reqPerSecColumns, "1000 req/sec", formatReqSec);
+ gen("#proxy-req-per-sec-chart", proxyColumns, "req/sec");
gen("#max-latency-chart", maxLatencyColumns, "milliseconds", logScale);
gen("#max-memory-chart", maxMemoryColumns, "megabytes", formatMB);
gen("#binary-size-chart", binarySizeColumns, "megabytes", formatMB);
diff --git a/website/benchmarks.html b/website/benchmarks.html
index a307fcdad..fa1b140e9 100644
--- a/website/benchmarks.html
+++ b/website/benchmarks.html
@@ -3,17 +3,18 @@
<html>
<head>
<title>Deno Benchmarks</title>
- <link rel="shortcut icon" href="favicon.ico">
+ <link rel="shortcut icon" href="favicon.ico" />
<link rel="stylesheet" href="https://unpkg.com/c3@0.6.7/c3.min.css" />
<link rel="stylesheet" href="style.css" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
</head>
<body>
<div id="spinner-overlay">
- <div class="spinner"></div>`
+ <div class="spinner"></div>
+ `
</div>
<main>
- <a href="/"><img src="images/deno_logo_4.gif" width=200></a>
+ <a href="/"><img src="images/deno_logo_4.gif" width="200"/></a>
<h1>Deno Continuous Benchmarks</h1>
<p>
@@ -21,7 +22,10 @@
<a href="https://github.com/denoland/deno">master branch</a>.
</p>
- <p>Make sure your adblocker is disabled as some can block the chart rendering.</p>
+ <p>
+ Make sure your adblocker is disabled as some can block the chart
+ rendering.
+ </p>
<p><a href="#recent">recent data</a></p>
<p><a href="#all">all data</a> (takes a moment to load)</p>
@@ -37,41 +41,53 @@
<li>
<a
href="https://github.com/denoland/deno/blob/master/tools/deno_tcp.ts"
- >deno_tcp</a>
+ >deno_tcp</a
+ >
is a fake http server that doesn't parse HTTP. It is comparable to
<a
href="https://github.com/denoland/deno/blob/master/tools/node_tcp.js"
- >node_tcp</a>
+ >node_tcp</a
+ >
.
</li>
<li>
<a
href="https://github.com/denoland/deno_std/blob/master/http/http_bench.ts"
- >deno_http</a>
+ >deno_http</a
+ >
is a web server written in TypeScript. It is comparable to
<a
href="https://github.com/denoland/deno/blob/master/tools/node_http.js"
- >node_http</a>
+ >node_http</a
+ >
.
</li>
- <li>deno_core_single and deno_core_multi are two versions of
- a minimal fake HTTP server. It blindly reads and writes fixed HTTP
- packets. It is comparable to deno_tcp and node_tcp.
- This is a standalone executable that uses <a
- href="https://crates.io/crates/deno">the deno rust crate</a>. The
+ <li>
+ deno_core_single and deno_core_multi are two versions of a minimal
+ fake HTTP server. It blindly reads and writes fixed HTTP packets. It
+ is comparable to deno_tcp and node_tcp. This is a standalone
+ executable that uses
+ <a href="https://crates.io/crates/deno">the deno rust crate</a>. The
code is in
<a
href="https://github.com/denoland/deno/blob/master/core/examples/http_bench.rs"
- >http_bench.rs</a>
+ >http_bench.rs</a
+ >
and
<a
href="https://github.com/denoland/deno/blob/master/core/examples/http_bench.js"
- >http_bench.js</a>. single uses <a
- href="https://docs.rs/tokio/0.1.19/tokio/runtime/current_thread/index.html">tokio::runtime::current_thread</a>
- and multi uses <a
- href="https://docs.rs/tokio/0.1.19/tokio/runtime/">tokio::runtime::threadpool</a>.
+ >http_bench.js</a
+ >. single uses
+ <a
+ href="https://docs.rs/tokio/0.1.19/tokio/runtime/current_thread/index.html"
+ >tokio::runtime::current_thread</a
+ >
+ and multi uses
+ <a href="https://docs.rs/tokio/0.1.19/tokio/runtime/"
+ >tokio::runtime::threadpool</a
+ >.
</li>
<li>
@@ -86,10 +102,60 @@
<div id="req-per-sec-chart"></div>
+ <h3 id="proxy-req-per-sec">
+ Proxy Req/Sec <a href="#proxy-eq-per-sec">#</a>
+ </h3>
+
+ <p>
+ Tests proxy performance. 10 keep-alive connections do as many
+ hello-world requests as possible. Bigger is better.
+ </p>
+
+ <ul>
+ <li>
+ <a
+ href="https://github.com/denoland/deno/blob/master/tools/deno_tcp_proxy.ts"
+ >deno_proxy_tcp</a
+ >
+ is a fake tcp proxy server that doesn't parse HTTP. It is comparable
+ to
+ <a
+ href="https://github.com/denoland/deno/blob/master/tools/node_tcp_proxy.js"
+ >node_proxy_tcp</a
+ >
+ .
+ </li>
+
+ <li>
+ <a
+ href="https://github.com/denoland/deno/blob/master/tools/deno_http_proxy.ts"
+ >deno_proxy</a
+ >
+ is an HTTP proxy server written in TypeScript. It is comparable to
+ <a
+ href="https://github.com/denoland/deno/blob/master/tools/node_http_proxy.js"
+ >node_proxy</a
+ >
+ .
+ </li>
+
+ <li>
+ <a
+ href="https://github.com/denoland/deno/blob/master/tools/hyper_hello.rs"
+ >
+ hyper
+ </a>
+ is a Rust HTTP server used as the origin for the proxy tests
+ </li>
+ </ul>
+
+ <div id="proxy-req-per-sec-chart"></div>
+
<h3 id="max-latency">Max Latency <a href="#max-latency">#</a></h3>
<p>
- Max latency during the same test used above for requests/second. Smaller is better.
+ Max latency during the same test used above for requests/second. Smaller
+ is better.
</p>
<div id="max-latency-chart"></div>
@@ -101,8 +167,8 @@
<a
href="https://github.com/denoland/deno/blob/master/tests/002_hello.ts"
>
- tests/002_hello.ts
- </a>,
+ tests/002_hello.ts </a
+ >,
<a
href="https://github.com/denoland/deno/blob/master/tests/003_relative_import.ts"
>tests/003_relative_import.ts</a
@@ -167,26 +233,28 @@
<script type="module">
import { drawCharts } from "./app.js";
window.chartWidth = 800;
- const overlay = document.getElementById("spinner-overlay")
+ const overlay = document.getElementById("spinner-overlay");
- function showSpinner () {
+ function showSpinner() {
overlay.style.display = "block";
}
- function hideSpinner () {
+ function hideSpinner() {
overlay.style.display = "none";
}
- function updateCharts () {
- const u = window.location.hash.match("all") ? "./data.json" : "recent.json";
+ function updateCharts() {
+ const u = window.location.hash.match("all")
+ ? "./data.json"
+ : "recent.json";
+
+ showSpinner();
- showSpinner()
-
- drawCharts(u).finally(hideSpinner)
+ drawCharts(u).finally(hideSpinner);
}
- updateCharts()
+ updateCharts();
- window.onhashchange = updateCharts
+ window.onhashchange = updateCharts;
</script>
</body>
</html>