...
This commit is contained in:
56
bench/http-server.zig
Normal file
56
bench/http-server.zig
Normal file
@@ -0,0 +1,56 @@
|
||||
// Zig HTTP server — equivalent to 32-http-server.sx
|
||||
// Single-threaded, blocking, static response. Raw C sockets.
|
||||
|
||||
const std = @import("std");
|
||||
const c = @cImport({
|
||||
@cInclude("sys/socket.h");
|
||||
@cInclude("netinet/in.h");
|
||||
@cInclude("unistd.h");
|
||||
});
|
||||
|
||||
const body = "<html><body><h1>Hello from zig!</h1></body></html>";
|
||||
const response = "HTTP/1.1 200 OK\r\n" ++
|
||||
"Content-Type: text/html\r\n" ++
|
||||
"Connection: close\r\n" ++
|
||||
std.fmt.comptimePrint("Content-Length: {d}\r\n", .{body.len}) ++
|
||||
"\r\n" ++ body;
|
||||
|
||||
pub fn main() !void {
|
||||
const port: u16 = 8081;
|
||||
|
||||
const fd = c.socket(c.AF_INET, c.SOCK_STREAM, 0);
|
||||
if (fd < 0) return error.SocketFailed;
|
||||
|
||||
var opt: c_int = 1;
|
||||
_ = c.setsockopt(fd, c.SOL_SOCKET, c.SO_REUSEADDR, &opt, @sizeOf(c_int));
|
||||
|
||||
var addr: c.struct_sockaddr_in = std.mem.zeroes(c.struct_sockaddr_in);
|
||||
addr.sin_len = @sizeOf(c.struct_sockaddr_in);
|
||||
addr.sin_family = c.AF_INET;
|
||||
addr.sin_port = std.mem.nativeToBig(u16, port);
|
||||
addr.sin_addr.s_addr = 0;
|
||||
|
||||
if (c.bind(fd, @ptrCast(&addr), @sizeOf(c.struct_sockaddr_in)) < 0)
|
||||
return error.BindFailed;
|
||||
|
||||
if (c.listen(fd, 10) < 0)
|
||||
return error.ListenFailed;
|
||||
|
||||
std.debug.print("listening on http://localhost:{d}\n", .{port});
|
||||
|
||||
var count: u64 = 0;
|
||||
while (true) {
|
||||
const client = c.accept(fd, null, null);
|
||||
if (client < 0) continue;
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
_ = c.read(client, &buf, buf.len);
|
||||
_ = c.write(client, response.ptr, response.len);
|
||||
_ = c.close(client);
|
||||
|
||||
count += 1;
|
||||
if (count % 10000 == 0) {
|
||||
std.debug.print("[http] served {d} requests\n", .{count});
|
||||
}
|
||||
}
|
||||
}
|
||||
91
bench/run.sh
Executable file
91
bench/run.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# Benchmark: sx vs zig HTTP server
|
||||
# Usage: bash bench/run.sh [requests] [concurrency]
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
REQUESTS=${1:-10000}
|
||||
CONCURRENCY=${2:-50}
|
||||
WARMUP=500
|
||||
|
||||
SX_PORT=8080
|
||||
ZIG_PORT=8081
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
RESET='\033[0m'
|
||||
|
||||
cleanup() {
|
||||
[[ -n "$SX_PID" ]] && kill "$SX_PID" 2>/dev/null || true
|
||||
[[ -n "$ZIG_PID" ]] && kill "$ZIG_PID" 2>/dev/null || true
|
||||
wait 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo -e "${BOLD}=== sx vs zig HTTP server benchmark ===${RESET}"
|
||||
echo -e "requests: ${CYAN}$REQUESTS${RESET} concurrency: ${CYAN}$CONCURRENCY${RESET}"
|
||||
echo ""
|
||||
|
||||
# --- Build ---
|
||||
echo -e "${BOLD}Building sx server...${RESET}"
|
||||
time_sx_build=$( { time ./zig-out/bin/sx build examples/32-http-server.sx -o bench/sx-server 2>&1; } 2>&1 | tail -1 )
|
||||
echo " $time_sx_build"
|
||||
|
||||
echo -e "${BOLD}Building zig server...${RESET}"
|
||||
time_zig_build=$( { time zig build-exe bench/http-server.zig -O ReleaseFast -femit-bin=bench/zig-server 2>&1; } 2>&1 | tail -1 )
|
||||
echo " $time_zig_build"
|
||||
echo ""
|
||||
|
||||
wait_for_port() {
|
||||
local port=$1
|
||||
for i in $(seq 1 30); do
|
||||
if curl -s -o /dev/null "http://127.0.0.1:$port" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
echo "FAIL: server on port $port did not start" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
run_bench() {
|
||||
local name=$1
|
||||
local port=$2
|
||||
local color=$3
|
||||
|
||||
echo -e "${BOLD}${color}--- $name (port $port) ---${RESET}"
|
||||
|
||||
# Warmup
|
||||
ab -n $WARMUP -c 10 -q "http://127.0.0.1:$port/" > /dev/null 2>&1 || true
|
||||
|
||||
# Benchmark
|
||||
ab -n "$REQUESTS" -c "$CONCURRENCY" -q "http://127.0.0.1:$port/" 2>/dev/null | \
|
||||
grep -E '(Requests per second|Time per request|Transfer rate|Total transferred|Failed requests|Time taken)'
|
||||
echo ""
|
||||
}
|
||||
|
||||
# --- sx server ---
|
||||
bench/sx-server > /dev/null 2>&1 &
|
||||
SX_PID=$!
|
||||
wait_for_port $SX_PORT
|
||||
|
||||
run_bench "sx" $SX_PORT "$GREEN"
|
||||
|
||||
kill "$SX_PID" 2>/dev/null; wait "$SX_PID" 2>/dev/null || true
|
||||
SX_PID=""
|
||||
sleep 0.5
|
||||
|
||||
# --- zig server ---
|
||||
bench/zig-server > /dev/null 2>&1 &
|
||||
ZIG_PID=$!
|
||||
wait_for_port $ZIG_PORT
|
||||
|
||||
run_bench "zig" $ZIG_PORT "$CYAN"
|
||||
|
||||
kill "$ZIG_PID" 2>/dev/null; wait "$ZIG_PID" 2>/dev/null || true
|
||||
ZIG_PID=""
|
||||
|
||||
echo -e "${BOLD}Done.${RESET}"
|
||||
@@ -298,73 +298,6 @@ any_to_string :: (val: Any) -> string {
|
||||
result;
|
||||
}
|
||||
|
||||
build_print :: (fmt: string) -> string {
|
||||
code := "result := \"\"; ";
|
||||
seg_start := 0;
|
||||
i := 0;
|
||||
arg_idx := 0;
|
||||
while i < fmt.len {
|
||||
if fmt[i] == 123 {
|
||||
if i + 1 < fmt.len {
|
||||
if fmt[i + 1] == 125 {
|
||||
if i > seg_start {
|
||||
code = concat(code, "result = concat(result, substr(fmt, ");
|
||||
code = concat(code, int_to_string(seg_start));
|
||||
code = concat(code, ", ");
|
||||
code = concat(code, int_to_string(i - seg_start));
|
||||
code = concat(code, ")); ");
|
||||
}
|
||||
code = concat(code, "result = concat(result, any_to_string(args[");
|
||||
code = concat(code, int_to_string(arg_idx));
|
||||
code = concat(code, "])); ");
|
||||
arg_idx += 1;
|
||||
i += 2;
|
||||
seg_start = i;
|
||||
} else if fmt[i + 1] == 123 {
|
||||
code = concat(code, "result = concat(result, substr(fmt, ");
|
||||
code = concat(code, int_to_string(seg_start));
|
||||
code = concat(code, ", ");
|
||||
code = concat(code, int_to_string(i - seg_start + 1));
|
||||
code = concat(code, ")); ");
|
||||
i += 2;
|
||||
seg_start = i;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
} else if fmt[i] == 125 {
|
||||
if i + 1 < fmt.len {
|
||||
if fmt[i + 1] == 125 {
|
||||
code = concat(code, "result = concat(result, substr(fmt, ");
|
||||
code = concat(code, int_to_string(seg_start));
|
||||
code = concat(code, ", ");
|
||||
code = concat(code, int_to_string(i - seg_start + 1));
|
||||
code = concat(code, ")); ");
|
||||
i += 2;
|
||||
seg_start = i;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if seg_start < fmt.len {
|
||||
code = concat(code, "result = concat(result, substr(fmt, ");
|
||||
code = concat(code, int_to_string(seg_start));
|
||||
code = concat(code, ", ");
|
||||
code = concat(code, int_to_string(fmt.len - seg_start));
|
||||
code = concat(code, ")); ");
|
||||
}
|
||||
code = concat(code, "out(result);");
|
||||
code;
|
||||
}
|
||||
|
||||
build_format :: (fmt: string) -> string {
|
||||
code := "result := \"\"; ";
|
||||
seg_start := 0;
|
||||
@@ -428,7 +361,6 @@ build_format :: (fmt: string) -> string {
|
||||
code = concat(code, int_to_string(fmt.len - seg_start));
|
||||
code = concat(code, ")); ");
|
||||
}
|
||||
code = concat(code, "result;");
|
||||
code;
|
||||
}
|
||||
|
||||
@@ -437,7 +369,8 @@ format :: ($fmt: string, args: ..Any) -> string {
|
||||
}
|
||||
|
||||
print :: ($fmt: string, args: ..Any) {
|
||||
#insert build_print(fmt);
|
||||
#insert build_format(fmt);
|
||||
#insert "out(result);";
|
||||
}
|
||||
|
||||
List :: struct ($T: Type) {
|
||||
|
||||
Reference in New Issue
Block a user