Skip to content

Commit 3c3f098

Browse files
authored
Merge branch 'main' into fix/microlora-ewc
2 parents f086097 + bbe0ff8 commit 3c3f098

336 files changed

Lines changed: 81699 additions & 659 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
"""Prepare native Criterion executables, enumerate cases, or run one exact case.
3+
4+
Full characterization is opt-in: one case per job retains Criterion's configured
5+
sample count while excluding compilation from the 30/45 minute measurement budget.
6+
"""
7+
import argparse
8+
import json
9+
import os
10+
from pathlib import Path
11+
import re
12+
import shutil
13+
import subprocess
14+
15+
BENCHES = ("matmul_bench", "attention_bench", "ane_bench")
16+
17+
18+
def case_names(binary):
19+
result = subprocess.run([str(binary.resolve()), "--list"], check=True,
20+
capture_output=True, text=True)
21+
names = [line.removesuffix(": benchmark") for line in result.stdout.splitlines()
22+
if line.endswith(": benchmark")]
23+
if not names or len(names) != len(set(names)) or len(names) > 256:
24+
raise ValueError(f"Invalid case inventory for {binary}: {len(names)} (limit 256)")
25+
return names
26+
27+
28+
def prepare(features):
29+
benches = BENCHES if features else BENCHES[:2]
30+
command = ["cargo", "bench", "--locked", "-p", "ruvllm", "--no-run",
31+
"--message-format=json"]
32+
for bench in benches:
33+
command.extend(["--bench", bench])
34+
if features:
35+
command.extend(["--features", features])
36+
result = subprocess.run(command, check=True, stdout=subprocess.PIPE, text=True)
37+
directory = Path("benchmark-binaries")
38+
directory.mkdir(exist_ok=True)
39+
executables = {}
40+
for line in result.stdout.splitlines():
41+
message = json.loads(line)
42+
if message.get("reason") == "compiler-artifact" and message.get("executable"):
43+
name = message["target"]["name"]
44+
if name in benches:
45+
executables[name] = message["executable"]
46+
if set(executables) != set(benches):
47+
raise ValueError(f"Missing benchmark executables: {executables}")
48+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
49+
for bench in benches:
50+
binary = directory / bench
51+
shutil.copy2(executables[bench], binary)
52+
names = case_names(binary)
53+
(directory / f"{bench}.cases.json").write_text(json.dumps(names, indent=2))
54+
matrix = {"include": [{"case": name} for name in names]}
55+
output.write(f"{bench}={json.dumps(matrix, separators=(',', ':'))}\n")
56+
print(f"{bench}: {len(names)} independently budgeted cases")
57+
58+
59+
def main():
60+
parser = argparse.ArgumentParser(description=__doc__)
61+
parser.add_argument("mode", choices=("prepare", "run"))
62+
parser.add_argument("--features", default="")
63+
parser.add_argument("--bench", choices=BENCHES)
64+
args = parser.parse_args()
65+
if args.mode == "prepare":
66+
prepare(args.features)
67+
else:
68+
if not args.bench:
69+
parser.error("run requires --bench")
70+
binary = Path("benchmark-binaries") / args.bench
71+
name = os.environ["BENCHMARK_CASE"]
72+
inventory = json.loads((binary.parent / f"{args.bench}.cases.json").read_text())
73+
if name not in inventory:
74+
raise ValueError("Requested case is absent from the built inventory")
75+
binary.chmod(binary.stat().st_mode | 0o100)
76+
subprocess.run([str(binary.resolve()), "--bench", f"^{re.escape(name)}$",
77+
"--output-format", "bencher"], check=True)
78+
79+
80+
if __name__ == "__main__":
81+
main()

‎.github/workflows/build-gnn.yml‎

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,26 @@ on:
1010
tags:
1111
- 'v*'
1212
pull_request:
13-
branches: [main]
1413
paths:
1514
- 'crates/ruvector-gnn/**'
1615
- 'crates/ruvector-gnn-node/**'
16+
- '.github/workflows/build-gnn.yml'
1717
workflow_dispatch:
1818
inputs:
19+
update_binaries:
20+
description: 'Commit generated binaries to this branch (independent of publishing)'
21+
required: false
22+
type: boolean
23+
default: false
1924
publish:
2025
description: 'Publish to npm after build'
2126
required: false
2227
type: boolean
2328
default: false
2429

30+
permissions:
31+
contents: read
32+
2533
env:
2634
CARGO_TERM_COLOR: always
2735

@@ -40,7 +48,7 @@ jobs:
4048
- host: ubuntu-22.04
4149
target: aarch64-unknown-linux-gnu
4250
platform: linux-arm64-gnu
43-
- host: macos-14
51+
- host: macos-15-intel
4452
target: x86_64-apple-darwin
4553
platform: darwin-x64
4654
- host: macos-14
@@ -109,18 +117,42 @@ jobs:
109117
run: |
110118
mkdir -p gnn-artifacts/${{ matrix.settings.platform }}
111119
112-
# Find the built .node file
113-
NODE_FILE=$(find crates/ruvector-gnn-node -name "ruvector-gnn.*.node" -type f | head -1)
120+
# napi --platform writes this exact matrix target at the crate root.
121+
# Never select a checked-in npm/* binary through a recursive search.
122+
NODE_FILE="crates/ruvector-gnn-node/ruvector-gnn.${{ matrix.settings.platform }}.node"
114123
115-
if [ -z "$NODE_FILE" ]; then
116-
echo "ERROR: No .node file found"
124+
if [ ! -f "$NODE_FILE" ]; then
125+
echo "ERROR: Expected fresh matrix artifact $NODE_FILE"
117126
find crates/ruvector-gnn-node -name "*.node" -type f
118127
exit 1
119128
fi
120129
121130
echo "Found: $NODE_FILE"
122131
cp -v "$NODE_FILE" "gnn-artifacts/${{ matrix.settings.platform }}/"
123132
133+
- name: Test native binding on matching host
134+
shell: bash
135+
env:
136+
GNN_PLATFORM: ${{ matrix.settings.platform }}
137+
run: |
138+
NODE_HOST=$(node -p 'process.platform + "-" + process.arch')
139+
case "$GNN_PLATFORM" in
140+
darwin-x64) test "$NODE_HOST" = darwin-x64 ;;
141+
darwin-arm64) test "$NODE_HOST" = darwin-arm64 ;;
142+
esac
143+
case "$GNN_PLATFORM:$NODE_HOST" in
144+
linux-x64-gnu:linux-x64|darwin-x64:darwin-x64|darwin-arm64:darwin-arm64|win32-x64-msvc:win32-x64)
145+
;;
146+
*)
147+
echo "Cross-built artifact $GNN_PLATFORM cannot run on $NODE_HOST; runtime tests require a matching host."
148+
exit 0
149+
;;
150+
esac
151+
NODE_FILE=$(find "gnn-artifacts/$GNN_PLATFORM" -maxdepth 1 -name '*.node' -type f | head -1)
152+
test -n "$NODE_FILE"
153+
ulimit -c 0 || true
154+
RUVECTOR_GNN_BINDING="$(pwd)/$NODE_FILE" node --test crates/ruvector-gnn-node/test/*.test.js
155+
124156
- name: Upload artifact
125157
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
126158
with:
@@ -132,7 +164,7 @@ jobs:
132164
name: Commit Built GNN Binaries
133165
runs-on: ubuntu-22.04
134166
needs: build
135-
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main')
167+
if: github.event_name == 'workflow_dispatch' && inputs.update_binaries == true
136168
permissions:
137169
contents: write
138170

@@ -191,10 +223,10 @@ jobs:
191223
publish:
192224
name: Publish GNN Platform Packages
193225
runs-on: ubuntu-22.04
194-
needs: [build, commit-binaries]
226+
needs: build
195227
if: |
196-
inputs.publish == true ||
197-
startsWith(github.ref, 'refs/tags/v')
228+
(github.event_name == 'workflow_dispatch' && inputs.publish == true) ||
229+
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))
198230
199231
steps:
200232
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262

‎.github/workflows/python-wheels.yml‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,18 +253,27 @@ jobs:
253253
path: ${{ env.PACKAGE_DIR }}/dist/*.tar.gz
254254

255255
macos:
256-
runs-on: macos-14
256+
runs-on: ${{ matrix.runner }}
257257
name: macOS ${{ matrix.target }}
258258
strategy:
259259
fail-fast: false
260260
matrix:
261-
target: [x86_64-apple-darwin, aarch64-apple-darwin]
261+
include:
262+
- target: x86_64-apple-darwin
263+
runner: macos-15-intel
264+
architecture: x64
265+
machine: x86_64
266+
- target: aarch64-apple-darwin
267+
runner: macos-14
268+
architecture: arm64
269+
machine: arm64
262270
steps:
263271
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
264272

265273
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b
266274
with:
267275
python-version: '3.12'
276+
architecture: ${{ matrix.architecture }}
268277

269278
- name: Build abi3 wheel
270279
uses: PyO3/maturin-action@423a6347767a8b16e65c2a7a7042e4a921528da8
@@ -273,12 +282,13 @@ jobs:
273282
target: ${{ matrix.target }}
274283
args: --release --out dist
275284

276-
- name: Test wheel (native arch only)
277-
if: matrix.target == 'aarch64-apple-darwin'
285+
- name: Test wheel on matching native architecture
278286
working-directory: ${{ env.PACKAGE_DIR }}
279287
run: |
288+
python3 -c 'import platform; assert platform.machine() == "${{ matrix.machine }}", platform.machine()'
280289
python3 -m venv /tmp/wheel-venv
281-
/tmp/wheel-venv/bin/pip install dist/*.whl pytest numpy
290+
/tmp/wheel-venv/bin/python -m pip install --only-binary=:all: dist/*.whl pytest numpy
291+
/tmp/wheel-venv/bin/python -c 'import platform, ruvector; assert platform.machine() == "${{ matrix.machine }}", platform.machine()'
282292
/tmp/wheel-venv/bin/python -m pytest tests/test_smoke.py tests/test_collection.py tests/test_portfolio_extensions.py tests/test_native_rvf.py -q
283293
284294
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02

0 commit comments

Comments
 (0)