diff --git a/proto-metal/main.swift b/proto-metal/main.swift index 6c7e48823..e7c55d127 100644 --- a/proto-metal/main.swift +++ b/proto-metal/main.swift @@ -3162,7 +3162,9 @@ func servePackDataset(_ gpu: GPU, _ store: ServeStore, dayHex: String, dir: Stri guard let fillFn = lib.makeFunction(name: "igneum_cache_fill"), let buildFn = lib.makeFunction(name: "igneum_build") else { throw refuse("memhard.metal lacks igneum_cache_fill or igneum_build") } let fillPipe = try gpu.device.makeComputePipelineState(function: fillFn) let buildPipe = try gpu.device.makeComputePipelineState(function: buildFn) - let words = 1 << datasetLog2 + // ds55 (the class v6 research packs, 8 October 2026): a non-power-of-two dataset carries IGNEUM_DATASET_WORDS (the + // buffer's size) and IGNEUM_DATASET_ITEMS beside IGNEUM_DATASET_LOG2 (the floor); the kernel text maps (src * words) >> 32 + let words = defineU64("IGNEUM_DATASET_WORDS").map { Int($0) } ?? (1 << datasetLog2) // Class v5 (docs/design/class-v5-stored-state.md, 7 October 2026): the window's state leaves, leaves.bin beside program.h // (IGNEUM_STATE_LEAVES leaves of 16 little-endian words), checked against the pack's count and FNV-1a 64 // (IGNEUM_STATE_LEAVES_FNV64) and bound as buffer 2 of igneum_build with the count in buffer 3 (packbench.swift's shape, diff --git a/proto-metal/packbench.swift b/proto-metal/packbench.swift index a382ddd7d..6e8a8fa88 100644 --- a/proto-metal/packbench.swift +++ b/proto-metal/packbench.swift @@ -93,8 +93,12 @@ let cacheFnvWant = hex64(vj["cache_fnv1a64"] as! String) let hotFnvWant: UInt64? = (vj["hot_fnv1a64"] as? String).map(hex64) guard let device = MTLCreateSystemDefaultDevice(), let queue = device.makeCommandQueue() else { fail("no Metal device") } -let words = 1 << datasetLog2 -let mask = UInt32(words - 1) +// ds55 (the class v6 research packs, 8 October 2026): a non-power-of-two dataset carries IGNEUM_DATASET_WORDS and +// IGNEUM_DATASET_ITEMS beside IGNEUM_DATASET_LOG2 (the floor); the buffer is sized by the words, the items by ITEMS, +// and the kernel text maps an address as (src * words) >> 32, so the mask is the kernel's business, not this host's +let datasetWordsDefine = defineU64("IGNEUM_DATASET_WORDS") +let words = datasetWordsDefine.map { Int($0) } ?? (1 << datasetLog2) +let mask = UInt32(truncatingIfNeeded: (1 << datasetLog2) - 1) let cacheWords = 1 << cacheLog2 func compile(_ file: String) -> MTLLibrary { @@ -138,7 +142,7 @@ func fnv1a64(_ p: UnsafeRawPointer, _ n: Int) -> UInt64 { for i in 0.. -- bash -# tools/class-v5/kits-on-box.sh ...` with the label "v5 kit". +# tools/class-v5/${KITS_BOX_SCRIPT:-kits-on-box.sh} ...` with the label "v5 kit". set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BS_TOOL=class-v5-kits @@ -34,9 +34,9 @@ ZIP="/srv/artefacts/packs/packs-ca3-v5-$STAMP.zip" bs_toolchain_check bs_sync_sources bs_log "sources at $WT (commit $BS_SHA on $BS_BRANCH); building the class v5 kits on box $BOX" -# the body runs from a script FILE at the worktree's mirror checkout (tools/class-v5/kits-on-box.sh, carried by the commit), never +# the body runs from a script FILE at the worktree's mirror checkout (tools/class-v5/${KITS_BOX_SCRIPT:-kits-on-box.sh}, carried by the commit), never # an inline string: main's rule of 7 October 2026, 21:33 BST (no rm, find -delete or truncation inside a bash -c string) -CMD="cd '$WT' && bash tools/class-v5/kits-on-box.sh '$STAMP' '$SKIP_EMU'" +CMD="cd '$WT' && bash tools/class-v5/${KITS_BOX_SCRIPT:-kits-on-box.sh} '$STAMP' '$SKIP_EMU'" set +e BR_KIND=build BR_COMMAND="class v5 kits (packfile-test, emu --check v5, Linux and Windows workers, kit zip)" BR_TARGET="x86_64-linux+windows" BR_ARTEFACTS="" \ bs_remote_run "$WT" "$BS_WT class-v5 kits" "$CMD" 2>&1 | tee "/tmp/v5-kits-$STAMP.log" | grep -E '^(STEP|FAIL|KIT|BIN|LOGS|check PASS|self-test|packfile-test|emu:| v5|build-remote: RESULT)' diff --git a/tools/class-v5/kits-v6-on-box.sh b/tools/class-v5/kits-v6-on-box.sh new file mode 100755 index 000000000..49d00e663 --- /dev/null +++ b/tools/class-v5/kits-v6-on-box.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# The class v5 kit build as it runs ON igneum-build-1 (called by tools/class-v5/kits-remote.sh through the build-server +# library's remote runner, from the worktree's mirror checkout, so this file travels with the commit). A script file by +# path, never an inline string: main's rule of 7 October 2026, 21:33 BST (no rm, find -delete or truncation inside a +# `bash -c` string; tools/ci/inline-rm-check.sh). What it does, in order: the pack loader test with the class v5 cases, +# the Linux and Windows builds of the two one-click workers, the CPU emulation's --check on the class v5 pack, then the +# kit zip at /srv/artefacts/packs/packs-ca3-v5-.zip with SHA256SUMS. Logs land in /srv/builds/_log/v5-class/kits-. +# bash tools/class-v5/kits-on-box.sh (cwd = the worktree root on the box) +set -u +STAMP="${1:?stamp}"; SKIP_EMU="${2:-0}" +ZIP="/srv/artefacts/packs/packs-class-v6-$STAMP.zip" +LOGDIR="/srv/builds/_log/v5-class/kits-$STAMP" +T=$(mktemp -d /tmp/v5-kits.XXXXXX); S=$T/stage; mkdir -p "$S/bin/linux" "$S/bin/windows" "$S/src" "$S/packs" "$S/tools" "$LOGDIR" +rc=0 +step() { echo "STEP $1 $(date -u +%H:%M:%SZ)"; } +fail() { echo "FAIL $1"; rc=1; } +finish() { cp "$T"/*.log "$LOGDIR"/ 2>/dev/null; echo "LOGS $LOGDIR"; rm -rf "$T"; } +# class v6 (8 October 2026, the coordinator's order): the all-together pack and its known-failed partner come from the hash +# lane's tgz files on build-1 (V6_ALL and V6_PARTNER, default hl-v6-all and hl-v6-foldrw), extracted beside the stage; +# the emulation check runs pack A = the all pack against pack B = the partner, and the two ids must differ +V6_ALL="${V6_ALL:-hl-v6-all}"; V6_PARTNER="${V6_PARTNER:-hl-v6-foldrw}" +PK=$T/v6packs; mkdir -p "$PK" +for p in "$V6_ALL" "$V6_PARTNER"; do tar -xzf "/srv/artefacts/packs/$p.tgz" -C "$PK" 2>/dev/null || { echo "FAIL no pack tgz /srv/artefacts/packs/$p.tgz"; exit 1; }; done +rm -rf "$PK"/._* "$PK"/*/._* 2>/dev/null +V5="$PK/$V6_ALL"; V6P="$PK/$V6_PARTNER" +echo "PACKS $V6_ALL id $(grep -oE 'IGNEUM_PROGRAM_ID 0x[0-9a-f]+' "$V5/program.h" | awk '{print $2}') | $V6_PARTNER id $(grep -oE 'IGNEUM_PROGRAM_ID 0x[0-9a-f]+' "$V6P/program.h" | awk '{print $2}')" +[ "$(grep -oE 'IGNEUM_PROGRAM_ID 0x[0-9a-f]+' "$V5/program.h")" != "$(grep -oE 'IGNEUM_PROGRAM_ID 0x[0-9a-f]+' "$V6P/program.h")" ] || { echo "FAIL the all pack and its partner carry one id"; exit 1; } +step packfile-test +cc -std=c99 -Wall -Wextra -Wno-unused-function -O1 -o "$T/packfile-test" proto-cuda/nvrtc/emu/packfile-test.c && "$T/packfile-test" proto-cuda/packs/igneum-devnet-v4-epoch0 "$V5" > "$T/packfile-test.log" 2>&1 || fail "packfile-test ($(tail -1 "$T/packfile-test.log"))" +grep -E '^(FAIL| v5 pack)' "$T/packfile-test.log"; grep -c '^ok' "$T/packfile-test.log" | sed 's/^/packfile-test ok lines /' +step linux-opencl-worker +gcc -std=c99 -O2 -Wall -Wextra -Wno-stringop-truncation -Wno-format-truncation -DIGNEUM_CL_DYNAMIC -DCL_TARGET_OPENCL_VERSION=120 -I proto-cuda/packs/igneum-devnet-v4-epoch0 -DIGNEUM_KERNEL_PATH='"kernel_bound.cl"' -o "$S/bin/linux/igneum-worker-opencl" proto-opencl/host.c -ldl -lpthread 2> "$T/cl-linux.log" || { fail "linux opencl worker"; head -20 "$T/cl-linux.log"; } +step linux-cuda-worker +g++ -std=c++17 -O2 -Wall -Wextra -I proto-cuda/nvrtc -I /usr/local/cuda/include -o "$S/bin/linux/igneum-worker-cuda" proto-cuda/nvrtc/worker.cpp -ldl -lpthread 2> "$T/cuda-linux.log" || { fail "linux cuda worker"; head -20 "$T/cuda-linux.log"; } +step windows-cuda-worker +x86_64-w64-mingw32-g++ -std=c++17 -O2 -Wall -Wextra -static -I proto-cuda/nvrtc -I /usr/local/cuda/include -o "$S/bin/windows/igneum-worker-cuda.exe" proto-cuda/nvrtc/worker.cpp 2> "$T/cuda-win.log" && x86_64-w64-mingw32-strip "$S/bin/windows/igneum-worker-cuda.exe" || { fail "windows cuda worker"; head -20 "$T/cuda-win.log"; } +step windows-opencl-worker +# the Khronos CL headers alone (never -I /usr/include: that puts glibc's stdint.h ahead of mingw's) +mkdir -p "$T/inc" && cp -r /usr/include/CL "$T/inc/" +x86_64-w64-mingw32-gcc -std=c99 -O2 -Wall -Wextra -Wno-stringop-truncation -Wno-format-truncation -static -DIGNEUM_CL_DYNAMIC -DCL_TARGET_OPENCL_VERSION=120 -I "$T/inc" -I proto-cuda/packs/igneum-devnet-v4-epoch0 -DIGNEUM_KERNEL_PATH='"kernel_bound.cl"' -o "$S/bin/windows/igneum-worker-opencl.exe" proto-opencl/host.c 2> "$T/cl-win.log" && x86_64-w64-mingw32-strip "$S/bin/windows/igneum-worker-opencl.exe" || { fail "windows opencl worker"; head -20 "$T/cl-win.log"; } +if [ "$SKIP_EMU" = 0 ]; then + step emu-check-v5 + # the CPU emulation (proto-cuda/nvrtc/emu/test.sh's build) with pack A = the class v5 pack and pack B = the v4 control + E=$T/emu; mkdir -p "$E" + emu_kernel() { { echo '#include '; echo '#include '; echo "namespace $2 {"; sed -E 's/([A-Za-z_0-9]+)<<<([^,]+), ([^>]+)>>>\(/emu_launch(\1, \2, \3, /' "$1/$3.cu"; echo "}"; } > "$E/$3_$2.cpp"; g++ -std=c++17 -O2 -w -I proto-cuda/emu -I "$1" -c "$E/$3_$2.cpp" -o "$E/$3_$2.o"; } + emu_kernel "$V5" emu_pack_a kernel && emu_kernel "$V5" emu_pack_a kernel_bound && emu_kernel "$V6P" emu_pack_b kernel && emu_kernel "$V6P" emu_pack_b kernel_bound \ + && g++ -std=c++17 -O2 -Wall -Wextra -DIGNEUM_EMU -I proto-cuda/nvrtc -I /usr/local/cuda/include -c proto-cuda/nvrtc/worker.cpp -o "$E/worker.o" \ + && g++ -std=c++17 -O2 -Wall -Wextra -DIGNEUM_EMU -DIGNEUM_EMU_TWO_PACKS -I proto-cuda/nvrtc -I proto-cuda/emu -I /usr/local/cuda/include -c proto-cuda/nvrtc/emu/emu_backend.cpp -o "$E/emu_backend.o" \ + && g++ -std=c++17 -O2 -w -I proto-cuda/emu -c proto-cuda/emu/shim.cpp -o "$E/shim.o" \ + && g++ -o "$E/igneum-worker-cuda-emu" "$E"/*.o -pthread 2> "$T/emu-build.log" || { fail "emu build"; head -30 "$T/emu-build.log"; } + if [ -x "$E/igneum-worker-cuda-emu" ]; then + ( IGNEUM_EMU_PACK="$V5" IGNEUM_EMU_PACK2="$V6P" timeout 1500 nice -n 10 "$E/igneum-worker-cuda-emu" --check --pack "$V5" > "$T/emu-check.log" 2>&1 ) || fail "emu --check on the class v5 pack (rc $?)" + grep -E '^check PASS|self-test|FAIL|error' "$T/emu-check.log" | cut -c1-400 + fi +fi +if [ "$rc" != 0 ]; then echo "KIT not written: a step failed (rc $rc)"; finish; exit 1; fi +step stage +for p in "$V6_ALL" "$V6_PARTNER"; do mkdir -p "$S/packs/$p"; cp "$PK/$p"/* "$S/packs/$p/"; done +cp proto-cuda/nvrtc/worker.cpp proto-cuda/nvrtc/packfile.h proto-cuda/nvrtc/cuda_api.h proto-opencl/host.c proto-opencl/cl_dynamic.h proto-newpow/class-v5/bench.cu proto-newpow/class-v5/run.sh proto-metal/packbench.swift "$S/src/" +cp tools/class-v5/fleet-cuda-v5-bench.sh tools/class-v5/pc1-amd-v5-bench.ps1 "$S/tools/" +cat > "$S/README.txt" <<'R' +packs-class-v6: the class v6 kit (Igneum, 8 October 2026; docs/design/class-v5-stored-state.md section 0, the class v6 row): the all-together pack and its known-failed partner +packs/v5-dn3-epoch0 the first class v5 pack: Devnet 3 epoch 0, program id e5a4ac5978462156, 11 state leaves (leaves.bin, 704 B) under + state root 7e37a9fb19b154d32daf5bf30a50d339a75029fbc9eec9ea20e95439dba5a311, cache FNV-1a 64 7334fa46e5d972eb + expected fingerprint of the 2^24 outputs at base nonce 0: 82b19cbde8557ea5 (Metal, M5 Max, 18:07:09Z) on every platform +packs/v5-genesis the string-seed class v5 pack over the devnet's 93 leaves; packs/v4-genesis the class v4 control (sub-version 3) +bin/linux igneum-worker-cuda (NVRTC, libcuda + libnvrtc.so.12 at run time), igneum-worker-opencl (libOpenCL.so.1 at run time) +bin/windows igneum-worker-cuda.exe (nvcuda.dll + nvrtc64 at run time), igneum-worker-opencl.exe (OpenCL.dll at run time) +Every worker reads a class v5 pack's leaves.bin, checks it against the pack's IGNEUM_STATE_LEAVES_FNV64, uploads it for igneum_build and +frees it after the build; a pack without its leaves builds nothing. Self-test: cache head, last line and FNV; dataset head, last word and +64 samples; 96 vector lanes. Bench: igneum-worker-cuda --bench --pack packs/v5-dn3-epoch0 --batch-log2 24 (fleet-cuda-v5-bench.sh); +igneum-worker-opencl --bench-pack --pack packs/v5-dn3-epoch0 --batch-log2 24 --device (pc1-amd-v5-bench.ps1); src/bench.cu with nvcc. +Intel: not measured tonight (7 October 2026); no Arc B580 sits on PC 1 or PC 2 and the only Arc path needs a driver click, which no PC job +may raise; the card's holder and a click-free driver path are owed. The OpenCL worker takes the Intel device by index the same way. +R +( cd "$S" && find . -type f ! -name SHA256SUMS | sort | xargs sha256sum > SHA256SUMS ) +mkdir -p /srv/artefacts/packs +python3 - "$S" "$ZIP" <<'PY' +import os, sys, zipfile +stage, out = sys.argv[1], sys.argv[2] +with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z: + for dp, dn, fn in os.walk(stage): + dn.sort() + for f in sorted(fn): + p = os.path.join(dp, f); rel = os.path.relpath(p, stage) + zi = zipfile.ZipInfo(rel, date_time=(2026, 10, 7, 0, 0, 0)); zi.compress_type = zipfile.ZIP_DEFLATED + zi.external_attr = (0o755 if rel.startswith('bin/') or rel.endswith('.sh') else 0o644) << 16 + with open(p, 'rb') as fh: z.writestr(zi, fh.read()) +PY +sha256sum "$ZIP" | cut -c1-64 > "$ZIP.sha256" +echo "KIT $ZIP bytes $(stat -c %s "$ZIP") files $(python3 -c "import zipfile,sys; print(len(zipfile.ZipFile(sys.argv[1]).namelist()))" "$ZIP")" +echo "KIT sha256 $(cat "$ZIP.sha256")" +for b in "$S"/bin/linux/* "$S"/bin/windows/*; do echo "BIN $(basename "$(dirname "$b")")/$(basename "$b") $(stat -c %s "$b") bytes sha256 $(sha256sum "$b" | cut -c1-64)"; done +finish +exit 0