feat: HTTP registry, app/bench harnesses, and DWARF #line maps

Ship QUALITY_PLAN sessions 31–33: fetchable package index URLs, showcase
app and micro/nexus benchmarks, and debugger-friendly C codegen.

- Registry: BUX_REGISTRY accepts http(s) URLs (curl/wget → ~/.bux/cache)
- E.2: make test-apps smoke for nexus/boko/simpledb/jwt-pitbul
- E.5: benches/micro + C/Nim/Zig twins; make bench-nexus (wrk)
- E.4: HIR locs → #line .bux; default -O0 -g; --release -O2; make test-dwarf
This commit is contained in:
2026-07-19 22:46:45 +03:00
parent 53b43b0f79
commit eb81856565
26 changed files with 983 additions and 52 deletions
+56
View File
@@ -0,0 +1,56 @@
# Bux benchmarks (E.5)
Micro-kernels + optional Nexus HTTP throughput for regression and language comparison.
## Quick run
```bash
# From repo root
make bench # Bux micro + C + Nim (+ Zig if installed)
make bench-nexus # wrk vs apps/nexus /api/health
BENCH_NEXUS=1 make bench
```
## Micro suites
| Suite | Kernels | Build |
|-------|---------|-------|
| `micro/` | Bux: `int_loop`, `fib30`, `string_concat`, `array_push` | `buxc build` |
| `c/` | C: `fib30`, `int_loop` | `gcc -O2` |
| `nim/` | Nim twins | `nim c -d:release --opt:speed` |
| `zig/` | Zig twins (optional) | `zig build-exe -OReleaseFast` |
Output lines:
```
BENCH <name> iters=N total_us=T us_per_op=P
```
## Nexus throughput
`tools/bench_nexus.sh` / `make bench-nexus`:
1. Builds `apps/nexus`
2. Starts on `NEXUS_PORT` (default **18080**), bind `127.0.0.1`
3. `wrk -t4 -c64 -d5s` against `/api/health`
4. Prints `BENCH nexus_health rps=…`
Env knobs:
| Variable | Default | Meaning |
|----------|---------|---------|
| `NEXUS_PORT` | `18080` | Listen port (also used by the server binary) |
| `NEXUS_WORKERS` | `4` | Worker threads |
| `NEXUS_BENCH_DURATION` | `5s` | wrk `-d` |
| `NEXUS_BENCH_C` | `64` | wrk connections |
| `NEXUS_BENCH_T` | `4` | wrk threads |
Server-side (any nexus run):
- `NEXUS_PORT`, `NEXUS_WORKERS`, `NEXUS_BIND`, `NEXUS_PUBLIC`
## Notes
- Numbers vary by machine; use them relatively (same host, same day).
- Nexus currently closes connections (`Connection: close`) — RPS is honest for that model, not keep-alive maxed.
- Requires `wrk` for `bench-nexus` (`apt install wrk` on Debian/Ubuntu).
+26
View File
@@ -0,0 +1,26 @@
/* C reference: recursive fib(30) — compare with benches/micro fib30 */
#include <stdio.h>
#include <stdint.h>
#include <time.h>
static int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
static int64_t now_us(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000000LL + (int64_t)ts.tv_nsec / 1000LL;
}
int main(void) {
(void)fib(20); /* warmup */
int64_t t0 = now_us();
int r = fib(30);
int64_t t1 = now_us();
if (r < 0) return 1;
printf("BENCH fib30 iters=1 total_us=%lld us_per_op=%lld\n",
(long long)(t1 - t0), (long long)(t1 - t0));
return 0;
}
+24
View File
@@ -0,0 +1,24 @@
/* C reference: 1e6 integer arithmetic loop */
#include <stdio.h>
#include <stdint.h>
#include <time.h>
static int64_t now_us(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (int64_t)ts.tv_sec * 1000000LL + (int64_t)ts.tv_nsec / 1000LL;
}
int main(void) {
const int iters = 1000000;
int acc = 0;
int64_t t0 = now_us();
for (int i = 0; i < iters; i++) {
acc = acc + i * 3 - 1;
}
int64_t t1 = now_us();
if (acc == 0 && iters > 0) return 1;
printf("BENCH int_loop iters=%d total_us=%lld us_per_op=%lld\n",
iters, (long long)(t1 - t0), (long long)((t1 - t0) / iters));
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
[Package]
Name = "micro"
Version = "0.1.0"
Type = "bin"
Description = "Micro-benchmarks for Bux (E.5)"
[Build]
Output = "Bin"
+104
View File
@@ -0,0 +1,104 @@
// Micro-benchmarks (E.5) — recursive fib, string concat, array push.
// Prints lines: BENCH name iters total_us us_per_op
import Std::Io::{PrintLine, Print};
import Std::Time::{Time_NowUs};
import Std::String::{String_FromInt, String_Concat, String_Len};
import Std::Array::{Array, Array_New, Array_Push, Array_Len, Array_Get};
func Fib(n: int) -> int {
if n <= 1 {
return n;
}
return Fib(n - 1) + Fib(n - 2);
}
func Report(name: String, iters: int, usTotal: int64) {
var perOpUs: int64 = 0;
if iters > 0 {
perOpUs = usTotal / (iters as int64);
}
Print("BENCH ");
Print(name);
Print(" iters=");
Print(String_FromInt(iters as int64));
Print(" total_us=");
Print(String_FromInt(usTotal));
Print(" us_per_op=");
PrintLine(String_FromInt(perOpUs));
}
func BenchIntLoop() {
let iters: int = 1000000;
var acc: int = 0;
var i: int = 0;
let t0: int64 = Time_NowUs();
while i < iters {
acc = acc + i * 3 - 1;
i = i + 1;
}
let t1: int64 = Time_NowUs();
if acc == 0 && iters > 0 {
PrintLine("unreachable");
}
Report("int_loop", iters, t1 - t0);
}
func BenchFib() {
discard Fib(20);
let n: int = 30;
let t0: int64 = Time_NowUs();
let r: int = Fib(n);
let t1: int64 = Time_NowUs();
if r < 0 {
PrintLine("unreachable");
}
Report("fib30", 1, t1 - t0);
}
func BenchStringConcat() {
let iters: int = 50000;
var i: int = 0;
let t0: int64 = Time_NowUs();
var s: String = "";
while i < iters {
s = String_Concat(s, "x");
if String_Len(s) > 256 {
s = "x";
}
i = i + 1;
}
let t1: int64 = Time_NowUs();
if String_Len(s) < 0 {
PrintLine("unreachable");
}
Report("string_concat", iters, t1 - t0);
}
func BenchArrayPush() {
let iters: int = 100000;
var arr: Array<int> = Array_New<int>(16);
var i: int = 0;
let t0: int64 = Time_NowUs();
while i < iters {
Array_Push<int>(&arr, i);
i = i + 1;
}
let t1: int64 = Time_NowUs();
let n: uint = Array_Len<int>(&arr);
if n as int != iters {
Print("array len mismatch: ");
PrintLine(String_FromInt(n as int64));
}
discard Array_Get<int>(&arr, (iters - 1) as uint);
Report("array_push", iters, t1 - t0);
}
func Main() -> int {
PrintLine("=== Bux micro-benchmarks (E.5) ===");
BenchIntLoop();
BenchFib();
BenchStringConcat();
BenchArrayPush();
PrintLine("=== done ===");
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
## Nim twin of benches/c/fib.c — recursive fib(30)
import std/[times, strformat]
proc fib(n: int): int =
if n <= 1: return n
fib(n - 1) + fib(n - 2)
proc nowUs(): int64 =
int64(epochTime() * 1_000_000.0)
discard fib(20)
let t0 = nowUs()
let r = fib(30)
let t1 = nowUs()
if r < 0: quit(1)
echo &"BENCH fib30 iters=1 total_us={t1 - t0} us_per_op={t1 - t0}"
+15
View File
@@ -0,0 +1,15 @@
## Nim twin of benches/c/int_loop.c — 1e6 integer arithmetic loop
import std/[times, strformat]
proc nowUs(): int64 =
int64(epochTime() * 1_000_000.0)
const iters = 1_000_000
var acc = 0
let t0 = nowUs()
for i in 0 ..< iters:
acc = acc + i * 3 - 1
let t1 = nowUs()
if acc == 0 and iters > 0: quit(1)
let total = t1 - t0
echo &"BENCH int_loop iters={iters} total_us={total} us_per_op={total div iters}"
+22
View File
@@ -0,0 +1,22 @@
// Zig twin of benches/c/fib.c — recursive fib(30)
// Build: zig build-exe -OReleaseFast -femit-bin=build/fib fib.zig
const std = @import("std");
fn fib(n: i32) i32 {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
fn nowUs() i64 {
return @divTrunc(@as(i64, @intCast(std.time.nanoTimestamp())), 1000);
}
pub fn main() !void {
_ = fib(20);
const t0 = nowUs();
const r = fib(30);
const t1 = nowUs();
if (r < 0) return error.Unreachable;
const out = std.io.getStdOut().writer();
try out.print("BENCH fib30 iters=1 total_us={d} us_per_op={d}\n", .{ t1 - t0, t1 - t0 });
}
+22
View File
@@ -0,0 +1,22 @@
// Zig twin of benches/c/int_loop.c — 1e6 integer arithmetic loop
// Build: zig build-exe -OReleaseFast -femit-bin=build/int_loop int_loop.zig
const std = @import("std");
fn nowUs() i64 {
return @divTrunc(@as(i64, @intCast(std.time.nanoTimestamp())), 1000);
}
pub fn main() !void {
const iters: i32 = 1_000_000;
var acc: i32 = 0;
const t0 = nowUs();
var i: i32 = 0;
while (i < iters) : (i += 1) {
acc = acc + i * 3 - 1;
}
const t1 = nowUs();
if (acc == 0 and iters > 0) return error.Unreachable;
const total = t1 - t0;
const out = std.io.getStdOut().writer();
try out.print("BENCH int_loop iters={d} total_us={d} us_per_op={d}\n", .{ iters, total, @divTrunc(total, iters) });
}