diff --git a/frameworks/zix-http3/Dockerfile b/frameworks/zix-http3/Dockerfile index 661e75e4f..53dd98715 100644 --- a/frameworks/zix-http3/Dockerfile +++ b/frameworks/zix-http3/Dockerfile @@ -9,6 +9,7 @@ ARG ZIG_VERSION=0.16.0 ARG ZIX_VERSION=0.5.x-rc1 RUN apk add --no-cache ca-certificates curl git tar xz openssl +WORKDIR /server RUN set -eu; \ case "${TARGETARCH:-amd64}" in \ amd64) ZIG_ARCH=x86_64 ;; \ @@ -20,7 +21,6 @@ RUN set -eu; \ mv "/opt/zig-${ZIG_ARCH}-linux-${ZIG_VERSION}" /opt/zig ENV PATH="/opt/zig:${PATH}" -WORKDIR /server COPY build.zig build.zig.zon ./ COPY src ./src diff --git a/frameworks/zix-http3/src/main.zig b/frameworks/zix-http3/src/main.zig index 8ee95fc8e..623c3ceea 100644 --- a/frameworks/zix-http3/src/main.zig +++ b/frameworks/zix-http3/src/main.zig @@ -23,8 +23,6 @@ const DISPATCH_MODEL: zix.Http3.DispatchModel = .URING; const WORKERS: usize = 0; -// Same number as PORT on purpose: QUIC binds UDP 8443, the readiness probe -// arrives on TCP 8443 (the two coexist, distinct sockets). const PROBE_TCP_PORT: u16 = 8443; const TLS_CERT_DEFAULT: []const u8 = "/etc/zix-tls/server.crt"; @@ -32,64 +30,6 @@ const TLS_KEY_DEFAULT: []const u8 = "/etc/zix-tls/server.key"; // --------------------------------------------------------- // -/// Populate the static cache once at startup, -/// single-threaded, warming every candidate the handler probes (.br, .gz, identity) -/// so the request path only hits the lock-free lookup. -/// Without it the first request for each name inserts -/// under the spinlock while opening the file. -fn prewarmStatic() void { - var base_buf: [512]u8 = undefined; - var base = handler.g_static_base; - if (base.len > 1 and base[base.len - 1] == '/') base = base[0 .. base.len - 1]; - if (base.len >= base_buf.len) return; - - @memcpy(base_buf[0..base.len], base); - base_buf[base.len] = 0; - - const dir_fd = std.posix.openatZ(std.posix.AT.FDCWD, @ptrCast(&base_buf), .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, 0) catch return; - defer _ = std.posix.system.close(dir_fd); - - // Iterate with raw getdents64 (this std.fs has no portable Dir.iterate). - // linux_dirent64 layout: - // d_ino(8) d_off(8) d_reclen(2 @16) d_type(1 @18) d_name(@19, null-terminated). - var dbuf: [4096]u8 = undefined; - while (true) { - const rc = std.os.linux.getdents64(dir_fd, &dbuf, dbuf.len); - const got: isize = @bitCast(rc); - if (got <= 0) break; - - var off: usize = 0; - while (off < @as(usize, @intCast(got))) { - const reclen: usize = @as(usize, dbuf[off + 16]) | (@as(usize, dbuf[off + 17]) << 8); - const d_type = dbuf[off + 18]; - const name = std.mem.sliceTo(dbuf[off + 19 ..], 0); - off += reclen; - - if (d_type == 4) continue; // DT_DIR - if (name.len == 0 or name[0] == '.') continue; - - // Reduce a precompressed name to its base, - // then warm every candidate (.br, .gz, identity). - // A missing variant caches a null slot, - // so the request path never inserts under load. - var stem = name; - if (std.mem.endsWith(u8, stem, ".br")) stem = stem[0 .. stem.len - ".br".len] else if (std.mem.endsWith(u8, stem, ".gz")) stem = stem[0 .. stem.len - ".gz".len]; - if (stem.len == 0 or stem.len > handler.STATIC_NAME_MAX) continue; - - var cand_buf: [handler.STATIC_NAME_MAX + 3]u8 = undefined; - if (std.fmt.bufPrint(&cand_buf, "{s}.br", .{stem})) |c| { - _ = handler.resolveStatic(c); - } else |_| {} - if (std.fmt.bufPrint(&cand_buf, "{s}.gz", .{stem})) |c| { - _ = handler.resolveStatic(c); - } else |_| {} - _ = handler.resolveStatic(stem); - } - } -} - -// --------------------------------------------------------- // - // Answers the readiness probe (GET /baseline2?a=1&b=1 over TCP https). // Any 2xx body satisfies the probe curl, the value mirrors a=1&b=1. fn probeResponder(_: *const zix.Http1.ParsedHead, _: []const u8, fd: std.posix.fd_t) void { @@ -109,7 +49,7 @@ fn probeServer(io: std.Io, tls: *zix.Tls.Context) void { .ip = IP, .port = PROBE_TCP_PORT, .tls = tls, - .dispatch_model = .EPOLL, + .dispatch_model = DISPATCH_MODEL, .workers = 1, }); defer server.deinit(); @@ -125,16 +65,8 @@ const Routes = zix.Http3.Router(&[_]zix.Http3.Route{ }); pub fn main(process: std.process.Init) !void { - // Elevate scheduling priority (setpriority -19). Fails silently when the - // process lacks CAP_SYS_NICE, so no special capability is required for correctness. - _ = std.os.linux.syscall3(.setpriority, 0, 0, @as(usize, @bitCast(@as(isize, -19)))); - - // Warm the static cache before any worker serves, - // so the request path is lock-free (no spinlock - // held across a file open on the first request for each name). const data_dir = "/data"; handler.g_static_base = std.fmt.bufPrint(&handler.g_static_base_buf, "{s}/static/", .{data_dir}) catch "/data/static/"; - prewarmStatic(); var allocator_tls = std.heap.ArenaAllocator.init(std.heap.smp_allocator); defer allocator_tls.deinit(); diff --git a/site/data/results/zix.json b/site/data/results/zix.json index 67822789b..6331d2f29 100644 --- a/site/data/results/zix.json +++ b/site/data/results/zix.json @@ -44,18 +44,18 @@ "baseline-h3-64": { "framework": "zix", "language": "Zig", - "rps": 9573685, - "avg_latency": "413us", - "p99_latency": "757us", - "cpu": "1283.3%", - "memory": "416MiB", + "rps": 9392865, + "avg_latency": "420us", + "p99_latency": "766us", + "cpu": "1282.4%", + "memory": "413MiB", "connections": 64, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "73.19MB/s", + "bandwidth": "71.95MB/s", "reconnects": 0, - "status_2xx": 47964164, + "status_2xx": 47152183, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0 @@ -218,18 +218,18 @@ "static-h3-64": { "framework": "zix", "language": "Zig", - "rps": 611890, - "avg_latency": "3.31ms", - "p99_latency": "10.22ms", - "cpu": "2905.3%", - "memory": "403MiB", + "rps": 686550, + "avg_latency": "4.24ms", + "p99_latency": "18.10ms", + "cpu": "3343.4%", + "memory": "415MiB", "connections": 64, "threads": 64, "duration": "5s", "pipeline": 1, - "bandwidth": "9.29GB/s", + "bandwidth": "10.45GB/s", "reconnects": 0, - "status_2xx": 3065571, + "status_2xx": 3446483, "status_3xx": 0, "status_4xx": 0, "status_5xx": 0