nandi/vflutter_ffipublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/nandi/vflutter_ffi.git
git clone ssh://git@rickub.com/nandi/vflutter_ffi.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

check_pool.sh · 117 lines · 4.2 KBBash Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env bash
# Does the web build actually render in parallel, and does it still produce
# the right pixels?
#
# Both halves matter. A worker pool that silently falls back to one thread
# looks exactly like a working one except for the clock, and a pool that
# assembles bands in the wrong order produces a plausible-looking but wrong
# frame. So this times 1 worker against many and compares both against a
# straight single-instance render.
#
# The page POSTs its results back rather than logging them: headless Chrome's
# console plumbing is unreliable, and --virtual-time-budget would make
# performance.now() meaningless for timing anyway.
set -euo pipefail
cd "$(dirname "$0")/.."

chrome="${CHROME_EXECUTABLE:-$(command -v google-chrome-stable || command -v google-chrome || command -v chromium || true)}"
[ -n "$chrome" ] || { echo "no Chrome found; set CHROME_EXECUTABLE"; exit 1; }
[ -f example/web/nimflutter.js ] || { echo "run 'just wasm' first"; exit 1; }

work="$(mktemp -d)"
port="${PORT:-8793}"
profile="$work/profile"
trap 'rm -rf "$work"' EXIT

cp example/web/nimflutter.js example/web/nimflutter.wasm \
   example/web/nimflutter_worker.js example/web/nimflutter_pool.js "$work/"

cat > "$work/index.html" <<'HTML'
<!DOCTYPE html><meta charset="utf-8"><title>pool</title>
<script src="nimflutter.js"></script>
<script src="nimflutter_pool.js"></script>
<script type="module">
const W = 800, H = 600, ITER = 500;
const SIG = Array(10).fill('number');
const report = (lines) =>
  fetch('/result', {method: 'POST', body: lines.join('\n')});

try {
  // Ground truth: one instance, one band, on the main thread.
  const M = await createNimFlutterModule();
  M.ccall('nf_init', null, [], []);
  const n = W * H * 4;
  const p = M._malloc(n);
  const t0 = performance.now();
  M.ccall('nf_mandelbrot', null, SIG, [p, n, W, H, -0.5, 0.0, 3.0, ITER, 0, H]);
  const serialMs = performance.now() - t0;
  const truth = M.HEAPU8.slice(p, p + n);
  M._free(p);

  const timed = async (tiles) => {
    await nimflutterRenderParallel(W, H, -0.5, 0.0, 3.0, ITER, tiles); // warm
    const t = performance.now();
    const out = await nimflutterRenderParallel(W, H, -0.5, 0.0, 3.0, ITER, tiles);
    return {ms: performance.now() - t, out};
  };

  const diff = (a, b) => {
    if (a.length !== b.length) return -1;
    let d = 0;
    for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) d++;
    return d;
  };

  const one = await timed(1);
  const many = await timed(32);

  await report([
    `cores=${navigator.hardwareConcurrency} workers=${nimflutterPoolSize()}`,
    `main-thread=${serialMs.toFixed(1)}ms`,
    `1-worker=${one.ms.toFixed(1)}ms`,
    `32-bands=${many.ms.toFixed(1)}ms`,
    `speedup=${(one.ms / many.ms).toFixed(2)}x`,
    `diff-1worker=${diff(truth, one.out)}`,
    `diff-pool=${diff(truth, many.out)}`,
  ]);
} catch (e) {
  await report([`error=${e && e.message ? e.message : e}`]);
}
</script>
HTML

# Serves the directory, and exits after the page POSTs its results to /result.
cat > "$work/server.py" <<'PY'
import sys, http.server
out = sys.argv[1]
class H(http.server.SimpleHTTPRequestHandler):
    def do_POST(self):
        body = self.rfile.read(int(self.headers['Content-Length'])).decode()
        open(out, 'w').write(body)
        self.send_response(204); self.end_headers()
        raise SystemExit(0)
    def log_message(self, *a): pass
http.server.HTTPServer(('127.0.0.1', int(sys.argv[2])), H).serve_forever()
PY

(cd "$work" && exec python3 server.py results.txt "$port") >/dev/null 2>&1 &
sleep 2

"$chrome" --headless=new --no-sandbox --disable-dev-shm-usage \
  --user-data-dir="$profile" --disable-gpu \
  "http://127.0.0.1:$port/index.html" >/dev/null 2>&1 &
chrome_pid=$!

for _ in $(seq 1 60); do
  [ -s "$work/results.txt" ] && break
  sleep 1
done
kill "$chrome_pid" 2>/dev/null || true
kill %1 %2 2>/dev/null || true

[ -s "$work/results.txt" ] || { echo "FAIL page produced no result"; exit 1; }
{ cat "$work/results.txt"; echo; }
grep -q '^error=' "$work/results.txt" && { echo "FAIL page threw"; exit 1; }
grep -q 'diff-1worker=0' "$work/results.txt" || { echo "FAIL 1-worker frame differs"; exit 1; }
grep -q 'diff-pool=0' "$work/results.txt" || { echo "FAIL pooled frame differs"; exit 1; }
echo "ok  pooled and single-instance frames are identical"