-
Notifications
You must be signed in to change notification settings - Fork 47
465 lines (430 loc) · 18.6 KB
/
Copy pathci.yml
File metadata and controls
465 lines (430 loc) · 18.6 KB
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
pre-commit:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.0
with:
extra_args: --all-files
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-unit-tests
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install torch --index-url https://download.pytorch.org/whl/cpu
- name: Run golden unit tests
run: pytest tests/golden -v
detect-changes:
runs-on: ubuntu-latest
outputs:
has_changes: ${{ steps.check.outputs.has_changes }}
files: ${{ steps.check.outputs.files }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- id: check
run: |
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "has_changes=true" >> "$GITHUB_OUTPUT"
echo "files=" >> "$GITHUB_OUTPUT"
exit 0
fi
# Map the raw diff to the runnable scripts CI must exercise:
# - a changed models/ source file selects every runnable file that
# (transitively) imports it, not just the file itself;
# - any change outside models/ (golden/, .github/, docs, examples, …)
# selects all runnable examples as a smoke test.
# See .github/scripts/detect_changes.py for the rules.
FILES=$(git diff --name-only \
${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} \
| python .github/scripts/detect_changes.py)
if [ -n "$FILES" ]; then
echo "has_changes=true" >> "$GITHUB_OUTPUT"
else
echo "has_changes=false" >> "$GITHUB_OUTPUT"
fi
echo "files=$FILES" >> "$GITHUB_OUTPUT"
echo "Selected files: $FILES"
sim:
needs: detect-changes
if: github.event_name != 'pull_request' || needs.detect-changes.outputs.has_changes == 'true'
# Simulator job: no device needed, so it runs on the CPU runner inside the
# ci-py310 container (mirrors pypto's codegen-tests setup, registry on port
# 5001). pypto/ptoas/pto-isa are installed exactly like the on-device a2a3
# job — only the runner label and the lack of Ascend host env differ.
runs-on: [self-hosted, linux, arm64, cpu]
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
platform: [a2a3sim, a5sim]
env:
PTOAS_ROOT: ${{ github.workspace }}/ptoas-bin
PTO_ISA_ROOT: ${{ github.workspace }}/pto-isa
CMAKE_BUILD_PARALLEL_LEVEL: 16
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
CCACHE_DIR: /root/.cache/ccache
CCACHE_MAXSIZE: 30G
# Shared with the host-side a2a3 job (ci-runner) via the ci-cache mount;
# 000 keeps every cache file group/world-writable so root (this container)
# and ci-runner can both update and LRU-evict each other's entries.
CCACHE_UMASK: '000'
PIP_CACHE_DIR: /root/.cache/pip
container:
image: localhost:5001/ci-py310:latest
# GitHub Actions containers do not inherit the host's large /dev/shm; they
# use Docker's 64MB default. Distributed example kernels call torch
# share_memory_() and overflow it. --shm-size gives each container its own
# tmpfs (sized on demand, not pre-allocated), avoiding the --ipc=host name
# clashes between the concurrent cpu-1..4 runners.
options: >-
--shm-size=128g
-v /home/ci-runner/hw-native-sys-pypto-lib/ci-cache/pip-sim:/root/.cache/pip
-v /home/ci-runner/hw-native-sys-pypto-lib/ci-cache/ccache:/root/.cache/ccache
-v /home/ci-runner/hw-native-sys-pypto-lib/ci-cache/ptoas:/opt/ptoas-cache
steps:
- name: Checkout pypto-lib
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Resolve toolchain + clone pypto
# Clone the pypto we build against ONCE here (job-private /tmp — this CPU
# host runs the a2a3sim/a5sim matrix concurrently, so a shared tree would
# corrupt). The Build step below reuses this checkout, so the toolchain
# pins and the installed pypto are always the same revision. pypto owns
# ptoas (toolchain/versions.env) and pins pto-isa via its runtime submodule
# (runtime/pto_isa.pin); read both.
run: |
rm -rf /tmp/pypto
git clone --recurse-submodules --depth=1 https://github.com/hw-native-sys/pypto.git /tmp/pypto
{
grep -E '^PTOAS_VERSION=' /tmp/pypto/toolchain/versions.env
echo "PTOAS_SHA256=$(sed -n 's/^PTOAS_SHA256_AARCH64=//p' /tmp/pypto/toolchain/versions.env)"
echo "PTO_ISA_COMMIT=$(tr -d '[:space:]' < /tmp/pypto/runtime/pto_isa.pin)"
} >> "$GITHUB_ENV"
# Namespace the cache by pto-isa commit so a pto-isa bump can never serve
# stale objects compiled against the old ISA headers (see issue #1139):
# a different commit hashes to a different key, forcing a real recompile.
- name: Scope ccache to pto-isa version
run: echo "CCACHE_NAMESPACE=pto-isa-${PTO_ISA_COMMIT}" >> "$GITHUB_ENV"
- name: Show ccache stats (before)
run: ccache -s || true
- name: Clone pto-isa repository (pinned)
run: |
rm -rf "$PTO_ISA_ROOT"
timeout 60 git clone https://github.com/hw-native-sys/pto-isa.git "$PTO_ISA_ROOT" \
|| { rm -rf "$PTO_ISA_ROOT"; timeout 300 git clone https://gitcode.com/luohuan40/pto-isa.git "$PTO_ISA_ROOT"; }
cd "$PTO_ISA_ROOT"
git checkout ${{ env.PTO_ISA_COMMIT }}
- name: Build and install pypto and simpler
# Reuses /tmp/pypto cloned by the resolve step above (same revision the
# toolchain pins were read from).
run: |
python -m pip install --upgrade pip
pip install scikit-build-core nanobind cmake ninja
pip install --no-build-isolation /tmp/pypto
pip install --no-build-isolation /tmp/pypto/runtime
- name: Install runner dependencies
run: |
pip install nanobind
pip install torch
- name: Show ccache stats (after)
run: ccache -s || true
- name: Install ptoas (local cache)
env:
PTOAS_CACHE_DIR: /opt/ptoas-cache
run: |
CACHE_ARCHIVE="$PTOAS_CACHE_DIR/ptoas-aarch64-${PTOAS_VERSION}-${PTOAS_SHA256}.tar.gz"
download_ptoas() {
echo "Downloading ptoas ${PTOAS_VERSION}"
mkdir -p "$PTOAS_CACHE_DIR"
curl --fail --location --retry 3 --retry-all-errors \
https://github.com/hw-native-sys/PTOAS/releases/download/${PTOAS_VERSION}/ptoas-bin-aarch64.tar.gz \
-o "$CACHE_ARCHIVE.tmp"
echo "${PTOAS_SHA256} $CACHE_ARCHIVE.tmp" | sha256sum -c -
mv "$CACHE_ARCHIVE.tmp" "$CACHE_ARCHIVE"
}
if [ ! -f "$CACHE_ARCHIVE" ]; then
echo "Cache miss"
download_ptoas
elif ! echo "${PTOAS_SHA256} $CACHE_ARCHIVE" | sha256sum -c -; then
echo "Cache corrupted — removing and re-downloading"
rm -f "$CACHE_ARCHIVE"
download_ptoas
else
echo "Cache hit — using $CACHE_ARCHIVE"
fi
mkdir -p "$PTOAS_ROOT"
tar -xzf "$CACHE_ARCHIVE" -C "$PTOAS_ROOT"
chmod +x "$PTOAS_ROOT/ptoas"
chmod +x "$PTOAS_ROOT/bin/ptoas"
- name: Run ${{ matrix.platform }} tests
shell: bash
env:
PYTHONPATH: ${{ github.workspace }}
PTO2_RING_DEP_POOL: 16384
PTO2_RING_TASK_WINDOW: 16384
PTO2_RING_HEAP: 1073741824
run: |
set +e
if [ "${{ github.event_name }}" = "pull_request" ]; then
FILES="${{ needs.detect-changes.outputs.files }}"
else
FILES=$(find examples -name '*.py' ! -name '*draft*' | sort)
fi
if [ -z "$FILES" ]; then
echo "No runnable changed files; nothing to test."
exit 0
fi
failed=()
for f in $FILES; do
echo "::group::$f"
# Files marked `# ci: no-sim` (full multi-layer / multi-card
# forwards) are device-only and would just argparse-reject *sim.
if grep -qE '^#\s*ci:\s*no-sim' "$f"; then
echo "skip (no-sim): $f"
echo "::endgroup::"
continue
fi
if ! python "$f" -p ${{ matrix.platform }}; then
failed+=("$f")
echo "::error file=${f}::FAIL"
fi
echo "::endgroup::"
done
if [ ${#failed[@]} -ne 0 ]; then
printf 'FAILED: %s\n' "${failed[@]}"
exit 1
fi
echo "All selected files passed."
- name: Clean up build artifacts
if: always()
run: rm -rf build_output
a2a3:
needs: detect-changes
if: github.event_name != 'pull_request' || needs.detect-changes.outputs.has_changes == 'true'
# No container: multi-card kernels use HCCL, which needs a host-only env
# (set_env.sh, HCCL plugin paths) — in docker the chip child silent-crashes
# in comm_init. So this job runs directly on the host like pypto's
# dist-system-tests, on a 2-device runner so it can also schedule the
# multi-card files. Checkout / install happen under ./dist-checkout to avoid
# colliding with any container-owned workspace left on the shared runner.
runs-on: [self-hosted, linux, arm64, npu]
timeout-minutes: 30
defaults:
run:
working-directory: dist-checkout
env:
ASCEND_HOME_PATH: /usr/local/Ascend/cann-9.0.0
PTOAS_ROOT: ${{ github.workspace }}/dist-checkout/ptoas-bin
PTO_ISA_ROOT: ${{ github.workspace }}/dist-checkout/pto-isa
CMAKE_BUILD_PARALLEL_LEVEL: 16
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
CCACHE_DIR: /home/ci-runner/hw-native-sys-pypto-lib/ci-cache/ccache
CCACHE_MAXSIZE: 30G
# Shared with the container-side sim job (root) via the same ci-cache dir;
# 000 keeps cache files mutually writable across root and ci-runner.
CCACHE_UMASK: '000'
# Per-job pip dir: unlike ccache, pip has no shared-umask knob, so a shared
# pip cache would leave files owned by whichever job wrote first and block
# the other. The sim job uses ci-cache/pip-sim (see its bind mount).
PIP_CACHE_DIR: /home/ci-runner/hw-native-sys-pypto-lib/ci-cache/pip-a2a3
steps:
- name: Checkout pypto-lib
uses: actions/checkout@v4
with:
path: dist-checkout
persist-credentials: false
- name: Resolve toolchain + sync pypto source
# Sync the cached pypto checkout ONCE here; the Build step below reuses it,
# so the toolchain pins and the installed pypto are always the same
# revision. pypto owns ptoas (toolchain/versions.env) and pins pto-isa via
# its runtime submodule (runtime/pto_isa.pin); read both.
run: |
PYPTO_SRC="/home/ci-runner/hw-native-sys-pypto-lib/ci-cache/pypto-src"
if [ -d "$PYPTO_SRC/.git" ]; then
echo "Cache hit — updating pypto"
git -C "$PYPTO_SRC" fetch --depth=1 origin HEAD
git -C "$PYPTO_SRC" reset --hard FETCH_HEAD
git -C "$PYPTO_SRC" clean -ffdx
git -C "$PYPTO_SRC" submodule foreach --recursive 'git reset --hard && git clean -ffdx' || true
else
echo "Cache miss — cloning pypto"
git clone --recurse-submodules --depth=1 https://github.com/hw-native-sys/pypto.git "$PYPTO_SRC"
fi
git -C "$PYPTO_SRC" submodule update --init --recursive
rm -rf "$PYPTO_SRC/build" "$PYPTO_SRC/_skbuild" "$PYPTO_SRC/runtime/build"
{
grep -E '^PTOAS_VERSION=' "$PYPTO_SRC/toolchain/versions.env"
echo "PTOAS_SHA256=$(sed -n 's/^PTOAS_SHA256_AARCH64=//p' "$PYPTO_SRC/toolchain/versions.env")"
echo "PTO_ISA_COMMIT=$(tr -d '[:space:]' < "$PYPTO_SRC/runtime/pto_isa.pin")"
} >> "$GITHUB_ENV"
- name: Check NPU
working-directory: .
run: npu-smi info
# See the sim job: namespace the cache by pto-isa commit so an ISA bump
# forces a real recompile instead of reusing stale objects (issue #1139).
- name: Scope ccache to pto-isa version
run: echo "CCACHE_NAMESPACE=pto-isa-${PTO_ISA_COMMIT}" >> "$GITHUB_ENV"
- name: Clone pto-isa repository (pinned)
run: |
rm -rf "$PTO_ISA_ROOT"
timeout 60 git clone https://github.com/hw-native-sys/pto-isa.git "$PTO_ISA_ROOT" \
|| { rm -rf "$PTO_ISA_ROOT"; timeout 300 git clone https://gitcode.com/luohuan40/pto-isa.git "$PTO_ISA_ROOT"; }
cd "$PTO_ISA_ROOT"
git checkout ${{ env.PTO_ISA_COMMIT }}
- name: Bootstrap conda + per-job venv + write activate.sh
# Per-job venv layered on conda py310-lib; activate.sh lets each later step
# enter the env with a single `source activate.sh`. set_env.sh wires up
# the Ascend/HCCL host environment; LD_LIBRARY_PATH is prepended with
# $CONDA_PREFIX/lib because ptoas needs a newer GLIBCXX than the system.
run: |
source /home/ci-runner/miniconda3/etc/profile.d/conda.sh
conda activate py310-lib
python -m venv venv --system-site-packages
cat > activate.sh <<'EOF'
source /home/ci-runner/miniconda3/etc/profile.d/conda.sh
conda activate py310-lib
source "$GITHUB_WORKSPACE/dist-checkout/venv/bin/activate"
source /usr/local/Ascend/cann-9.0.0/set_env.sh
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$LD_LIBRARY_PATH"
EOF
- name: Show ccache stats (before)
run: ccache -s || true
- name: Build and install pypto and simpler
# Reuses the pypto source synced by the resolve step above (same revision
# the toolchain pins were read from).
run: |
source activate.sh
PYPTO_SRC="/home/ci-runner/hw-native-sys-pypto-lib/ci-cache/pypto-src"
python -m pip install --upgrade pip
pip install scikit-build-core nanobind cmake ninja
pip install --no-build-isolation "$PYPTO_SRC"
pip install --no-build-isolation "$PYPTO_SRC/runtime"
- name: Install runner dependencies
run: |
source activate.sh
pip install nanobind
pip install torch
- name: Show ccache stats (after)
run: ccache -s || true
- name: Install ptoas (local cache)
env:
PTOAS_CACHE_DIR: /home/ci-runner/hw-native-sys-pypto-lib/ci-cache/ptoas
run: |
CACHE_ARCHIVE="$PTOAS_CACHE_DIR/ptoas-aarch64-${PTOAS_VERSION}-${PTOAS_SHA256}.tar.gz"
download_ptoas() {
echo "Downloading ptoas ${PTOAS_VERSION}"
mkdir -p "$PTOAS_CACHE_DIR"
curl --fail --location --retry 3 --retry-all-errors \
https://github.com/hw-native-sys/PTOAS/releases/download/${PTOAS_VERSION}/ptoas-bin-aarch64.tar.gz \
-o "$CACHE_ARCHIVE.tmp"
echo "${PTOAS_SHA256} $CACHE_ARCHIVE.tmp" | sha256sum -c -
mv "$CACHE_ARCHIVE.tmp" "$CACHE_ARCHIVE"
}
if [ ! -f "$CACHE_ARCHIVE" ]; then
echo "Cache miss"
download_ptoas
elif ! echo "${PTOAS_SHA256} $CACHE_ARCHIVE" | sha256sum -c -; then
echo "Cache corrupted — removing and re-downloading"
rm -f "$CACHE_ARCHIVE"
download_ptoas
else
echo "Cache hit — using $CACHE_ARCHIVE"
fi
mkdir -p "$PTOAS_ROOT"
tar -xzf "$CACHE_ARCHIVE" -C "$PTOAS_ROOT"
chmod +x "$PTOAS_ROOT/ptoas"
chmod +x "$PTOAS_ROOT/bin/ptoas"
- name: Run a2a3 tests
shell: bash
env:
PYTHONPATH: ${{ github.workspace }}/dist-checkout
PTO2_RING_DEP_POOL: 16384
PTO2_RING_TASK_WINDOW: 16384
PTO2_RING_HEAP: 1073741824
run: |
source activate.sh
set +e
if [ "${{ github.event_name }}" = "pull_request" ]; then
FILES="${{ needs.detect-changes.outputs.files }}"
else
FILES=$(find examples -name '*.py' ! -name '*draft*' | sort)
fi
if [ -z "$FILES" ]; then
echo "No runnable changed files; nothing to test."
exit 0
fi
failed=()
for f in $FILES; do
echo "::group::$f"
# Files needing multiple NPUs declare `# ci: devices=N` (N>1) near
# the top; ring sizes come from the step env: above.
ndev=$(grep -oP '#\s*ci:\s*devices=\K[0-9]+' "$f" | head -1)
ndev=${ndev:-1}
if [ "$ndev" -gt 1 ]; then
if [ -z "$DEVICE_RANGE" ]; then
echo "::error file=${f}::needs $ndev devices but DEVICE_RANGE is unset"
failed+=("$f")
echo "::endgroup::"
continue
fi
actual_ndev=$(tr ',' '\n' <<<"$DEVICE_RANGE" | sed '/^$/d' | wc -l)
if [ "$actual_ndev" -lt "$ndev" ]; then
echo "::error file=${f}::needs $ndev devices but DEVICE_RANGE only provides $actual_ndev ($DEVICE_RANGE)"
failed+=("$f")
echo "::endgroup::"
continue
fi
python "$f" -p a2a3 -d "$(cut -d, -f1-"$ndev" <<<"$DEVICE_RANGE")"
else
python "$f" -p a2a3 -d "$DEVICE_ID"
fi
if [ $? -ne 0 ]; then
failed+=("$f")
echo "::error file=${f}::FAIL"
fi
echo "::endgroup::"
done
if [ ${#failed[@]} -ne 0 ]; then
printf 'FAILED: %s\n' "${failed[@]}"
exit 1
fi
echo "All selected files passed."
- name: Clean up build artifacts
if: always()
run: rm -rf build_output