This commit is contained in:
agra
2026-02-18 16:18:31 +02:00
parent 188ffed5af
commit 57cb01c3ec
3 changed files with 149 additions and 69 deletions

56
bench/http-server.zig Normal file
View 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
View 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}"

View File

@@ -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) {