Skip to content

Commit 5eb068f

Browse files
committed
perf(test): cap CPU at 85% via cpulimit/nice, reduce parallelism to 85% nproc
Keeps the machine responsive during test runs: - make test: wraps go test with cpulimit -l (nproc*85) when cpulimit is installed; falls back to nice -n 15 otherwise. -p and -parallel default to floor(nproc * 0.85) = 10 on a 12-core machine. - pre-commit hook: same CPU limiter + same 85% parallelism. CGO_ENABLED=0 is exported before the runner so it works with both cpulimit and nice (neither accepts env-var assignments as arguments). - Reset baseline to 60s (realistic for nice -n 15 + p=10). Override examples: make test # 85% CPU, auto parallelism make test TEST_P=4 # manual package concurrency Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 90b3141 commit 5eb068f

2 files changed

Lines changed: 48 additions & 22 deletions

File tree

.githooks/checks/02-unit-tests.sh

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,41 @@
11
#!/bin/sh
22
# L1: unit tests — fast, no external deps, no Docker.
33
#
4-
# Time guard: elapsed seconds are compared against .test-time-baseline.
5-
# The baseline file is committed so the reference survives machine changes.
4+
# CPU is capped at 85% so the machine stays responsive:
5+
# - cpulimit(1) if installed: hard CPU cap via kernel scheduler
6+
# - otherwise: nice -n 15 (lower scheduling priority)
67
#
7-
# .test-time-baseline — last recorded elapsed time (seconds). Updated on
8-
# every passing run. Fail if current > baseline * 1.5.
9-
#
10-
# To reset after an intentional perf change:
11-
# echo <new_seconds> > .test-time-baseline
12-
# git add .test-time-baseline && git commit -m "perf: reset test baseline"
8+
# Time guard: compare elapsed seconds against .test-time-baseline (committed).
9+
# Fail if current > baseline * 1.5. Update baseline on every passing run.
10+
# To reset: echo <new_seconds> > .test-time-baseline
1311

1412
REPO="$(git rev-parse --show-toplevel)"
1513
LOG_FILE="${REPO}/.test-fail.log"
1614
BASELINE_FILE="${REPO}/.test-time-baseline"
1715
rm -f "$LOG_FILE"
1816

19-
echo "pre-commit: running unit tests..."
17+
# Determine parallelism: 85% of nproc.
18+
NCPU=$(nproc)
19+
P85=$(( NCPU * 85 / 100 ))
20+
[ "$P85" -lt 1 ] && P85=1
21+
22+
# CPU limiter: cpulimit if available, else nice.
23+
if command -v cpulimit >/dev/null 2>&1; then
24+
LIMIT_PCT=$(( NCPU * 85 ))
25+
RUNNER="cpulimit -l ${LIMIT_PCT} --"
26+
else
27+
RUNNER="nice -n 15"
28+
fi
29+
30+
echo "pre-commit: running unit tests (cpu≤85%, p=${P85})..."
2031

32+
export CGO_ENABLED=0
2133
START=$(date +%s)
22-
if ! CGO_ENABLED=0 go test -timeout 180s -p "$(nproc)" -parallel "$(nproc)" ./... > "$LOG_FILE" 2>&1; then
34+
if ! $RUNNER go test \
35+
-timeout 180s \
36+
-p "${P85}" \
37+
-parallel "${P85}" \
38+
./... > "$LOG_FILE" 2>&1; then
2339
echo "" >&2
2440
echo "COMMIT BLOCKED: unit tests failed." >&2
2541
echo "" >&2
@@ -32,10 +48,10 @@ END=$(date +%s)
3248
ELAPSED=$((END - START))
3349
rm -f "$LOG_FILE"
3450

35-
# Time guard: compare against last successful run.
51+
# Time guard.
3652
if [ -f "$BASELINE_FILE" ]; then
3753
BASELINE=$(cat "$BASELINE_FILE")
38-
THRESHOLD=$((BASELINE + BASELINE / 2)) # 150% of baseline
54+
THRESHOLD=$((BASELINE + BASELINE / 2))
3955
if [ "$ELAPSED" -gt "$THRESHOLD" ]; then
4056
echo "" >&2
4157
echo "COMMIT BLOCKED: test time regression." >&2
@@ -47,6 +63,5 @@ if [ -f "$BASELINE_FILE" ]; then
4763
fi
4864
fi
4965

50-
# Update baseline to current run time.
5166
echo "$ELAPSED" > "$BASELINE_FILE"
5267
echo "pre-commit: unit tests passed (${ELAPSED}s)."

Makefile

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,21 @@ DAEMON_NAME = mxcli-daemon
3434
# Clean version for VS Code extension (must be valid semver: major.minor.patch)
3535
VSCE_VERSION = $(shell echo "$(VERSION)" | sed 's/^v//; s/-.*//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$$' || echo "0.0.0")
3636

37-
# Max parallel test packages (lower = less memory; override with: make test TEST_P=8)
38-
TEST_P ?= $(shell nproc)
39-
# Max concurrent tests within a package (default: GOMAXPROCS). Lower values
40-
# reduce peak memory and I/O pressure. Override: make test TEST_PARALLEL=4
41-
TEST_PARALLEL ?= $(shell nproc)
42-
# Hard ceiling on how long the full test suite may run. Fail fast rather
43-
# than hang CI. Override: make test TEST_TIMEOUT=300s
37+
# Limit CPU to 85% so the machine stays responsive during test runs.
38+
# Uses cpulimit(1) when installed; falls back to nice -n 15.
39+
# cpulimit -l value = nproc * 85 (percentage of one core per core).
40+
_NCPU := $(shell nproc)
41+
_CPU_LIMIT_L := $(shell echo "$(_NCPU) * 85" | bc)
42+
_CPU_RUNNER := $(shell command -v cpulimit >/dev/null 2>&1 \
43+
&& echo "cpulimit -l $(_CPU_LIMIT_L) --" \
44+
|| echo "nice -n 15")
45+
46+
# Max concurrent packages and tests, capped at 85% of nproc.
47+
_85PCT := $(shell echo "$(_NCPU) * 85 / 100" | bc)
48+
TEST_P ?= $(_85PCT)
49+
TEST_PARALLEL ?= $(_85PCT)
50+
51+
# Hard ceiling on how long the full test suite may run.
4452
TEST_TIMEOUT ?= 180s
4553

4654
.PHONY: build build-debug release clean test test-mdl report report-bench report-reset-baseline grammar sync-skills sync-commands sync-lint-rules sync-changelog sync-examples sync-all docs documentation docs-site docs-serve source-tree sbom sbom-report lint lint-go fmt vet update-helpdesk-golden test-helpdesk-regression setup
@@ -170,9 +178,12 @@ release: clean sync-all
170178
@echo " Launchers: mxcli-{os}-{arch}"
171179
@echo " Daemons: mxcli-daemon-{os}-{arch}.tar.zst (or .zip)"
172180

173-
# Run tests. TEST_TIMEOUT guards against hangs; TEST_P caps concurrent packages
174-
# to prevent I/O storms; TEST_PARALLEL caps concurrent tests within a package.
181+
# Run tests. CPU is capped at 85% via _CPU_RUNNER (cpulimit or nice -n 15)
182+
# so the machine stays responsive. TEST_P/TEST_PARALLEL default to 85% nproc.
175183
test:
184+
$(_CPU_RUNNER) $(MAKE) _test-inner
185+
186+
_test-inner:
176187
CGO_ENABLED=0 go test -timeout $(TEST_TIMEOUT) -p $(TEST_P) -parallel $(TEST_PARALLEL) ./...
177188

178189
# Run full test suite and generate layered report (terminal + HTML)

0 commit comments

Comments
 (0)