mew-bench 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mew_bench-0.1.0/.clang-format +3 -0
- mew_bench-0.1.0/.clangd +16 -0
- mew_bench-0.1.0/.dockerignore +18 -0
- mew_bench-0.1.0/.github/workflows/benchmark.yml.sketch +98 -0
- mew_bench-0.1.0/.github/workflows/ci.yml +117 -0
- mew_bench-0.1.0/.github/workflows/release.yml +86 -0
- mew_bench-0.1.0/.gitignore +44 -0
- mew_bench-0.1.0/.pre-commit-config.yaml +28 -0
- mew_bench-0.1.0/.readthedocs.yaml +23 -0
- mew_bench-0.1.0/CLAUDE.md +27 -0
- mew_bench-0.1.0/CMakeLists.txt +186 -0
- mew_bench-0.1.0/LICENSE +201 -0
- mew_bench-0.1.0/PKG-INFO +87 -0
- mew_bench-0.1.0/README.md +50 -0
- mew_bench-0.1.0/ROADMAP.md +326 -0
- mew_bench-0.1.0/benchmarks/bench_batched.py +22 -0
- mew_bench-0.1.0/benchmarks/bench_cpu.py +64 -0
- mew_bench-0.1.0/benchmarks/bench_memory.py +27 -0
- mew_bench-0.1.0/benchmarks/bench_threads.py +66 -0
- mew_bench-0.1.0/docker/profile.Dockerfile +42 -0
- mew_bench-0.1.0/docs/_static/.keep +0 -0
- mew_bench-0.1.0/docs/changelog.md +71 -0
- mew_bench-0.1.0/docs/conf.py +148 -0
- mew_bench-0.1.0/docs/development/building.md +142 -0
- mew_bench-0.1.0/docs/development/contributing.md +72 -0
- mew_bench-0.1.0/docs/getting-started/concepts.md +65 -0
- mew_bench-0.1.0/docs/getting-started/installation.md +97 -0
- mew_bench-0.1.0/docs/getting-started/quickstart.md +67 -0
- mew_bench-0.1.0/docs/guide/cli.md +181 -0
- mew_bench-0.1.0/docs/guide/configuration.md +47 -0
- mew_bench-0.1.0/docs/guide/context.md +66 -0
- mew_bench-0.1.0/docs/guide/parametrize-product.md +92 -0
- mew_bench-0.1.0/docs/guide/profiling-cpu.md +52 -0
- mew_bench-0.1.0/docs/guide/profiling-memory.md +45 -0
- mew_bench-0.1.0/docs/guide/profiling-native.md +187 -0
- mew_bench-0.1.0/docs/guide/regressions.md +131 -0
- mew_bench-0.1.0/docs/guide/reporters.md +117 -0
- mew_bench-0.1.0/docs/guide/state-and-timing.md +125 -0
- mew_bench-0.1.0/docs/guide/trusting-results.md +132 -0
- mew_bench-0.1.0/docs/guide/variants.md +106 -0
- mew_bench-0.1.0/docs/guide/writing-benchmarks.md +86 -0
- mew_bench-0.1.0/docs/index.md +113 -0
- mew_bench-0.1.0/docs/reference/api/compare.md +6 -0
- mew_bench-0.1.0/docs/reference/api/config.md +6 -0
- mew_bench-0.1.0/docs/reference/api/context.md +10 -0
- mew_bench-0.1.0/docs/reference/api/decorators.md +16 -0
- mew_bench-0.1.0/docs/reference/api/index.md +50 -0
- mew_bench-0.1.0/docs/reference/api/profiling.md +13 -0
- mew_bench-0.1.0/docs/reference/api/registry.md +18 -0
- mew_bench-0.1.0/docs/reference/api/regressions.md +6 -0
- mew_bench-0.1.0/docs/reference/api/reporters.md +23 -0
- mew_bench-0.1.0/docs/reference/api/runner.md +15 -0
- mew_bench-0.1.0/docs/reference/api/state.md +13 -0
- mew_bench-0.1.0/docs/reference/cli.md +46 -0
- mew_bench-0.1.0/pyproject.toml +162 -0
- mew_bench-0.1.0/scripts/asan-pytest.sh +38 -0
- mew_bench-0.1.0/scripts/tsan-pytest.sh +41 -0
- mew_bench-0.1.0/src/_mew/interrupt.h +17 -0
- mew_bench-0.1.0/src/_mew/module.cpp +27 -0
- mew_bench-0.1.0/src/_mew/registry.cpp +125 -0
- mew_bench-0.1.0/src/_mew/reporter.cpp +234 -0
- mew_bench-0.1.0/src/_mew/state.cpp +162 -0
- mew_bench-0.1.0/src/_mew/stubgen.pat +5 -0
- mew_bench-0.1.0/src/mew/__init__.py +54 -0
- mew_bench-0.1.0/src/mew/_completion_cache.py +96 -0
- mew_bench-0.1.0/src/mew/_completions.py +291 -0
- mew_bench-0.1.0/src/mew/_console.py +186 -0
- mew_bench-0.1.0/src/mew/_core.pyi +333 -0
- mew_bench-0.1.0/src/mew/_discovery.py +129 -0
- mew_bench-0.1.0/src/mew/_profile.py +208 -0
- mew_bench-0.1.0/src/mew/_registry.py +192 -0
- mew_bench-0.1.0/src/mew/_session.py +86 -0
- mew_bench-0.1.0/src/mew/_statistics.py +109 -0
- mew_bench-0.1.0/src/mew/_subprocess_worker.py +57 -0
- mew_bench-0.1.0/src/mew/_typing.py +138 -0
- mew_bench-0.1.0/src/mew/_variant_worker.py +115 -0
- mew_bench-0.1.0/src/mew/_variants.py +281 -0
- mew_bench-0.1.0/src/mew/api.py +484 -0
- mew_bench-0.1.0/src/mew/cli.py +1135 -0
- mew_bench-0.1.0/src/mew/compare.py +978 -0
- mew_bench-0.1.0/src/mew/config.py +142 -0
- mew_bench-0.1.0/src/mew/context.py +116 -0
- mew_bench-0.1.0/src/mew/cpu.py +205 -0
- mew_bench-0.1.0/src/mew/memory.py +211 -0
- mew_bench-0.1.0/src/mew/profilers/__init__.py +62 -0
- mew_bench-0.1.0/src/mew/profilers/_xctrace_export.py +215 -0
- mew_bench-0.1.0/src/mew/profilers/base.py +110 -0
- mew_bench-0.1.0/src/mew/profilers/perf.py +113 -0
- mew_bench-0.1.0/src/mew/profilers/pyspy.py +83 -0
- mew_bench-0.1.0/src/mew/profilers/xctrace.py +155 -0
- mew_bench-0.1.0/src/mew/py.typed +0 -0
- mew_bench-0.1.0/src/mew/regressions.py +278 -0
- mew_bench-0.1.0/src/mew/reporter.py +469 -0
- mew_bench-0.1.0/src/mew/runner.py +355 -0
- mew_bench-0.1.0/tests/_helpers.py +101 -0
- mew_bench-0.1.0/tests/conftest.py +17 -0
- mew_bench-0.1.0/tests/smoke_wheel.py +40 -0
- mew_bench-0.1.0/tests/test_api.py +426 -0
- mew_bench-0.1.0/tests/test_cli.py +840 -0
- mew_bench-0.1.0/tests/test_compare.py +943 -0
- mew_bench-0.1.0/tests/test_config.py +109 -0
- mew_bench-0.1.0/tests/test_console.py +116 -0
- mew_bench-0.1.0/tests/test_context.py +191 -0
- mew_bench-0.1.0/tests/test_discovery.py +146 -0
- mew_bench-0.1.0/tests/test_profile.py +475 -0
- mew_bench-0.1.0/tests/test_profilers.py +409 -0
- mew_bench-0.1.0/tests/test_profilers_native.py +83 -0
- mew_bench-0.1.0/tests/test_registry.py +172 -0
- mew_bench-0.1.0/tests/test_regressions.py +293 -0
- mew_bench-0.1.0/tests/test_reporter.py +348 -0
- mew_bench-0.1.0/tests/test_runner.py +454 -0
- mew_bench-0.1.0/tests/test_session.py +239 -0
- mew_bench-0.1.0/tests/test_variants.py +343 -0
- mew_bench-0.1.0/tests/test_xctrace_export.py +95 -0
- mew_bench-0.1.0/uv.lock +1183 -0
mew_bench-0.1.0/.clangd
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# clangd configuration for Zed (and any clangd-driven LSP).
|
|
2
|
+
|
|
3
|
+
CompileFlags:
|
|
4
|
+
CompilationDatabase: .
|
|
5
|
+
Add:
|
|
6
|
+
- -std=c++17
|
|
7
|
+
- -Wno-unknown-warning-option
|
|
8
|
+
|
|
9
|
+
Diagnostics:
|
|
10
|
+
# Google Benchmark uses some deprecated patterns internally; silencing
|
|
11
|
+
# those keeps the inline diagnostics focused on our own code.
|
|
12
|
+
Suppress:
|
|
13
|
+
- deprecated-declarations
|
|
14
|
+
|
|
15
|
+
Index:
|
|
16
|
+
Background: Build
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Keep the build context to source only. Honored by both `docker build` and
|
|
2
|
+
# Apple `container build` (BuildKit). Critically, the host `.venv` is macOS/wrong
|
|
3
|
+
# -arch and would corrupt the in-container `uv sync` — never copy it in.
|
|
4
|
+
.venv/
|
|
5
|
+
.venv-ft/
|
|
6
|
+
build/
|
|
7
|
+
.git/
|
|
8
|
+
.jj/
|
|
9
|
+
docs/_build/
|
|
10
|
+
|
|
11
|
+
# Caches and local artifacts — irrelevant to the build.
|
|
12
|
+
**/__pycache__/
|
|
13
|
+
**/*.pyc
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.mew-traces/
|
|
17
|
+
*.trace/
|
|
18
|
+
.DS_Store
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Sketch: benchmark a PR branch against master and post the comparison.
|
|
2
|
+
#
|
|
3
|
+
# Rename to `benchmark.yml` to activate. Notes before enabling:
|
|
4
|
+
# * Self-hosted (or at minimum a dedicated runner) is strongly recommended;
|
|
5
|
+
# GitHub-hosted runners share hardware and produce ±10-20% noise that
|
|
6
|
+
# swamps most real regressions.
|
|
7
|
+
# * Both runs must execute on the same physical machine for the comparison
|
|
8
|
+
# to be meaningful — keep them in one job, not two.
|
|
9
|
+
# * --repetitions 5 gives `mew compare` enough samples to compute
|
|
10
|
+
# a useful stddev. Tune up for noisier hardware.
|
|
11
|
+
|
|
12
|
+
name: benchmarks
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
pull_request:
|
|
16
|
+
paths:
|
|
17
|
+
- "src/**"
|
|
18
|
+
- "benchmarks/**"
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
pull-requests: write # for the sticky comment
|
|
23
|
+
|
|
24
|
+
concurrency:
|
|
25
|
+
group: benchmarks-${{ github.ref }}
|
|
26
|
+
cancel-in-progress: true
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
bench:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v6
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
|
|
37
|
+
- uses: astral-sh/setup-uv@v8
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.13"
|
|
40
|
+
enable-cache: true
|
|
41
|
+
|
|
42
|
+
# --- master baseline ---
|
|
43
|
+
- name: Check out master
|
|
44
|
+
run: git checkout origin/${{ github.base_ref }}
|
|
45
|
+
- name: Install (master)
|
|
46
|
+
run: uv sync --all-extras
|
|
47
|
+
- name: Benchmark master
|
|
48
|
+
run: uv run mew run benchmarks/ -o master.jsonl --repetitions 5
|
|
49
|
+
|
|
50
|
+
# --- PR head ---
|
|
51
|
+
- name: Check out PR head
|
|
52
|
+
run: git checkout ${{ github.event.pull_request.head.sha }}
|
|
53
|
+
- name: Install (PR)
|
|
54
|
+
run: uv sync --all-extras
|
|
55
|
+
- name: Benchmark PR
|
|
56
|
+
run: uv run mew run benchmarks/ -o pr.jsonl --repetitions 5
|
|
57
|
+
|
|
58
|
+
# --- compare ---
|
|
59
|
+
- name: Compare
|
|
60
|
+
# `--regression-threshold 5%` always prints the panel; add
|
|
61
|
+
# `--exit-non-zero-on-regression` once you trust the threshold to start
|
|
62
|
+
# gating merges. Per-benchmark allowlists live in
|
|
63
|
+
# `[tool.mew.regressions]` in pyproject.toml.
|
|
64
|
+
run: |
|
|
65
|
+
uv run mew compare pr.jsonl master.jsonl \
|
|
66
|
+
--stddev \
|
|
67
|
+
--regression-threshold 5% \
|
|
68
|
+
--exit-non-zero-on-regression \
|
|
69
|
+
| tee compare.txt
|
|
70
|
+
|
|
71
|
+
- uses: actions/upload-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: benchmarks
|
|
74
|
+
path: |
|
|
75
|
+
master.jsonl
|
|
76
|
+
pr.jsonl
|
|
77
|
+
compare.txt
|
|
78
|
+
|
|
79
|
+
- name: Render PR comment body
|
|
80
|
+
id: body
|
|
81
|
+
run: |
|
|
82
|
+
{
|
|
83
|
+
echo 'body<<EOF'
|
|
84
|
+
echo '## Benchmark comparison'
|
|
85
|
+
echo
|
|
86
|
+
echo 'Baseline: `master` · Variant: PR head'
|
|
87
|
+
echo
|
|
88
|
+
echo '```'
|
|
89
|
+
cat compare.txt
|
|
90
|
+
echo '```'
|
|
91
|
+
echo EOF
|
|
92
|
+
} >> "$GITHUB_OUTPUT"
|
|
93
|
+
|
|
94
|
+
- name: Comment on PR
|
|
95
|
+
uses: marocchino/sticky-pull-request-comment@v2
|
|
96
|
+
with:
|
|
97
|
+
header: mew-benchmarks
|
|
98
|
+
message: ${{ steps.body.outputs.body }}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
name: mew continuous integration checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
lint:
|
|
16
|
+
name: lint · prek + ty
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
20
|
+
with:
|
|
21
|
+
persist-credentials: false
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Run linters, formatter and type checker
|
|
30
|
+
run: uvx prek run --all-files --show-diff-on-failure
|
|
31
|
+
|
|
32
|
+
- name: Type check the codebase
|
|
33
|
+
run: uv run --all-extras --group test ty check
|
|
34
|
+
|
|
35
|
+
test:
|
|
36
|
+
name: ${{ matrix.os }} · py${{ matrix.python-version }}
|
|
37
|
+
runs-on: ${{ matrix.os }}
|
|
38
|
+
strategy:
|
|
39
|
+
fail-fast: false
|
|
40
|
+
matrix:
|
|
41
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
42
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
43
|
+
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
46
|
+
with:
|
|
47
|
+
persist-credentials: false
|
|
48
|
+
|
|
49
|
+
- name: Install uv
|
|
50
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
51
|
+
with:
|
|
52
|
+
python-version: ${{ matrix.python-version }}
|
|
53
|
+
enable-cache: true
|
|
54
|
+
|
|
55
|
+
- name: Install project and its dependencies
|
|
56
|
+
run: uv sync --all-extras --group test
|
|
57
|
+
|
|
58
|
+
- name: Run tests
|
|
59
|
+
run: uv run pytest -n auto
|
|
60
|
+
|
|
61
|
+
free-threaded:
|
|
62
|
+
name: free-threaded · py3.14t
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
66
|
+
with:
|
|
67
|
+
persist-credentials: false
|
|
68
|
+
|
|
69
|
+
- name: Install uv
|
|
70
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
71
|
+
with:
|
|
72
|
+
python-version: "3.14t"
|
|
73
|
+
enable-cache: true
|
|
74
|
+
|
|
75
|
+
# duckdb has no 3.14t wheel yet.
|
|
76
|
+
- name: Install project and its dependencies
|
|
77
|
+
run: uv sync --all-extras --group test --no-install-package duckdb
|
|
78
|
+
|
|
79
|
+
- name: Assert the GIL stays disabled after import
|
|
80
|
+
run: |
|
|
81
|
+
uv run --no-sync python - <<'PY'
|
|
82
|
+
import sys
|
|
83
|
+
assert not sys._is_gil_enabled(), "interpreter is not free-threaded"
|
|
84
|
+
import mew._core # FREE_THREADED build must not re-enable the GIL
|
|
85
|
+
assert not sys._is_gil_enabled(), "importing mew._core re-enabled the GIL"
|
|
86
|
+
print("OK: GIL disabled after `import mew._core`")
|
|
87
|
+
PY
|
|
88
|
+
|
|
89
|
+
- name: Run tests
|
|
90
|
+
run: uv run --no-sync pytest -n auto
|
|
91
|
+
|
|
92
|
+
docs:
|
|
93
|
+
name: docs · sphinx
|
|
94
|
+
runs-on: ubuntu-latest
|
|
95
|
+
steps:
|
|
96
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
97
|
+
with:
|
|
98
|
+
persist-credentials: false
|
|
99
|
+
|
|
100
|
+
- name: Install uv
|
|
101
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
102
|
+
with:
|
|
103
|
+
python-version: "3.12"
|
|
104
|
+
enable-cache: true
|
|
105
|
+
|
|
106
|
+
- name: Install project and docs dependencies
|
|
107
|
+
run: uv sync --all-extras --group docs
|
|
108
|
+
|
|
109
|
+
- name: Build docs (warnings = errors)
|
|
110
|
+
run: uv run sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
111
|
+
|
|
112
|
+
- name: Run doctests
|
|
113
|
+
run: uv run sphinx-build -b doctest docs docs/_build/doctest
|
|
114
|
+
|
|
115
|
+
- name: Check links
|
|
116
|
+
run: uv run sphinx-build -b linkcheck docs docs/_build/linkcheck
|
|
117
|
+
continue-on-error: true
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
name: Build wheels and upload to PyPI on release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
pull_request:
|
|
6
|
+
paths:
|
|
7
|
+
- pyproject.toml
|
|
8
|
+
- CMakeLists.txt
|
|
9
|
+
- .github/workflows/release.yml
|
|
10
|
+
release:
|
|
11
|
+
types: [published]
|
|
12
|
+
|
|
13
|
+
permissions: {}
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
wheels:
|
|
17
|
+
name: wheels · ${{ matrix.os }}
|
|
18
|
+
runs-on: ${{ matrix.os }}
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: false
|
|
32
|
+
|
|
33
|
+
- uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1
|
|
34
|
+
|
|
35
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
36
|
+
with:
|
|
37
|
+
name: wheels-${{ matrix.os }}
|
|
38
|
+
path: wheelhouse/*.whl
|
|
39
|
+
|
|
40
|
+
sdist:
|
|
41
|
+
name: sdist
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
45
|
+
with:
|
|
46
|
+
persist-credentials: false
|
|
47
|
+
|
|
48
|
+
- name: Install uv
|
|
49
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.12"
|
|
52
|
+
enable-cache: false
|
|
53
|
+
|
|
54
|
+
# `uv build` needs the backend importable without build isolation,
|
|
55
|
+
# which `no-build-isolation-package = ["mew-bench"]` asks for.
|
|
56
|
+
- run: uv sync --no-install-project
|
|
57
|
+
- run: uv build --sdist
|
|
58
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
59
|
+
with:
|
|
60
|
+
name: sdist
|
|
61
|
+
path: dist/*.tar.gz
|
|
62
|
+
|
|
63
|
+
publish:
|
|
64
|
+
name: publish · pypi
|
|
65
|
+
needs: [wheels, sdist]
|
|
66
|
+
if: github.event_name == 'release'
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
environment: pypi
|
|
69
|
+
permissions:
|
|
70
|
+
id-token: write
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
73
|
+
with:
|
|
74
|
+
path: dist
|
|
75
|
+
merge-multiple: true
|
|
76
|
+
- name: Assert every artifact matches the release tag
|
|
77
|
+
env:
|
|
78
|
+
TAG: ${{ github.event.release.tag_name }}
|
|
79
|
+
run: |
|
|
80
|
+
for f in dist/*; do
|
|
81
|
+
case "${f##*/}" in
|
|
82
|
+
mew_bench-"${TAG#v}"-*.whl | mew_bench-"${TAG#v}".tar.gz) ;;
|
|
83
|
+
*) echo "::error::${f##*/} is not version ${TAG#v}"; exit 1 ;;
|
|
84
|
+
esac
|
|
85
|
+
done
|
|
86
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
.venv-ft
|
|
12
|
+
|
|
13
|
+
# CMake / C++
|
|
14
|
+
.cache/
|
|
15
|
+
CMakeCache.txt
|
|
16
|
+
CMakeFiles/
|
|
17
|
+
cmake_install.cmake
|
|
18
|
+
Makefile
|
|
19
|
+
*.so
|
|
20
|
+
*.dylib
|
|
21
|
+
*.pyd
|
|
22
|
+
_skbuild/
|
|
23
|
+
compile_commands.json
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
.zed/
|
|
29
|
+
|
|
30
|
+
# macOS
|
|
31
|
+
.DS_Store
|
|
32
|
+
|
|
33
|
+
# Test/coverage
|
|
34
|
+
.pytest_cache/
|
|
35
|
+
.ruff_cache/
|
|
36
|
+
htmlcov/
|
|
37
|
+
.coverage
|
|
38
|
+
|
|
39
|
+
# flame graphs and profiles
|
|
40
|
+
*.html
|
|
41
|
+
|
|
42
|
+
# Sphinx
|
|
43
|
+
docs/_build/
|
|
44
|
+
docs/_generated/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.16.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-check
|
|
6
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/mirrors-clang-format
|
|
9
|
+
rev: v22.1.8
|
|
10
|
+
hooks:
|
|
11
|
+
- id: clang-format
|
|
12
|
+
types_or: [c++, c]
|
|
13
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
14
|
+
rev: v6.0.0
|
|
15
|
+
hooks:
|
|
16
|
+
- id: trailing-whitespace
|
|
17
|
+
- id: end-of-file-fixer
|
|
18
|
+
- id: check-yaml
|
|
19
|
+
- id: check-toml
|
|
20
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
21
|
+
rev: 0.12.0
|
|
22
|
+
hooks:
|
|
23
|
+
- id: uv-lock
|
|
24
|
+
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
|
25
|
+
rev: v1.28.0
|
|
26
|
+
hooks:
|
|
27
|
+
- id: zizmor
|
|
28
|
+
args: [--fix, --min-severity=medium]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Read the Docs configuration for mew.
|
|
2
|
+
# https://docs.readthedocs.com/platform/stable/config-file/v2.html
|
|
3
|
+
version: 2
|
|
4
|
+
|
|
5
|
+
build:
|
|
6
|
+
os: ubuntu-24.04
|
|
7
|
+
tools:
|
|
8
|
+
python: "3.12"
|
|
9
|
+
apt_packages:
|
|
10
|
+
- cmake
|
|
11
|
+
- ninja-build
|
|
12
|
+
jobs:
|
|
13
|
+
post_create_environment:
|
|
14
|
+
- pip install uv
|
|
15
|
+
install:
|
|
16
|
+
- UV_PROJECT_ENVIRONMENT=$READTHEDOCS_VIRTUALENV_PATH uv sync --group docs
|
|
17
|
+
|
|
18
|
+
sphinx:
|
|
19
|
+
configuration: docs/conf.py
|
|
20
|
+
fail_on_warning: true
|
|
21
|
+
|
|
22
|
+
formats:
|
|
23
|
+
- htmlzip
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Project Mew
|
|
2
|
+
|
|
3
|
+
Microbenchmarking Python code snippets, and reporting results.
|
|
4
|
+
|
|
5
|
+
## Tech stack
|
|
6
|
+
|
|
7
|
+
* Google Benchmark as a microbenchmarking library
|
|
8
|
+
* Python bindings via nanobind
|
|
9
|
+
* stdlib `argparse` for the CLI (no third-party CLI dependency)
|
|
10
|
+
* a small in-house ANSI module (`mew._console`) for terminal tables/colors — the runtime is zero-dependency
|
|
11
|
+
* vite + echarts as a visualization tool for a lightweight browser app
|
|
12
|
+
|
|
13
|
+
## Public Python API
|
|
14
|
+
|
|
15
|
+
Lightweight and small surface.
|
|
16
|
+
|
|
17
|
+
* Central: @mew.benchmark decorator to apply to a function to turn it into a benchmark.
|
|
18
|
+
* Need to pass a `State` object or similar that binds a `benchmark::State` C++ object.
|
|
19
|
+
* A @mew.parametrize decorator that defines a benchmark family mirroring the Google Benchmark concept. Needs to produce a sequence of callables that only take an input `State`, see previous.
|
|
20
|
+
* A reporter class that writes the results. This can be a wrapper around the C++ BenchmarkReporter class.
|
|
21
|
+
|
|
22
|
+
## Development setup
|
|
23
|
+
|
|
24
|
+
* Use `uv` and `uvx` for all things Python related, like dependency installation and removals, and linting.
|
|
25
|
+
* Prefer `prek` as a pre-commit runner tool.
|
|
26
|
+
* Add tests under tests/. Test the core functionality first.
|
|
27
|
+
* Use CMake for the Google Benchmark + nanobind setup, and `scikit-build-core` for PEP 517 wheel creation.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.21)
|
|
2
|
+
project(mew LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
7
|
+
|
|
8
|
+
# Emit compile_commands.json for clangd / IDE LSPs. Also force-set here in
|
|
9
|
+
# case the project is built without scikit-build-core (e.g. raw `cmake -B`).
|
|
10
|
+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
|
|
11
|
+
|
|
12
|
+
# The file lands in ${CMAKE_BINARY_DIR}; with build-dir = "build/{wheel_tag}"
|
|
13
|
+
# in pyproject.toml that path varies per ABI, so clangd's auto-discovery
|
|
14
|
+
# (which probes ./build/ and the source root) won't find it. Symlink it to
|
|
15
|
+
# the source root so IDE tooling picks it up without configuration.
|
|
16
|
+
add_custom_target(
|
|
17
|
+
symlink_compile_commands ALL
|
|
18
|
+
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
|
19
|
+
${CMAKE_BINARY_DIR}/compile_commands.json
|
|
20
|
+
${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json
|
|
21
|
+
BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json
|
|
22
|
+
COMMENT "Symlinking compile_commands.json to source root"
|
|
23
|
+
VERBATIM)
|
|
24
|
+
|
|
25
|
+
if(NOT CMAKE_BUILD_TYPE)
|
|
26
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
27
|
+
endif()
|
|
28
|
+
|
|
29
|
+
# AddressSanitizer build, driven by -DMEW_ASAN=ON or MEW_ASAN=1 in the env.
|
|
30
|
+
# `add_compile_options` covers the fetched googlebenchmark target so its
|
|
31
|
+
# objects stay instrumented; the per-target repeat below is required for
|
|
32
|
+
# nanobind's stubgen sanitizer detector.
|
|
33
|
+
option(MEW_ASAN "Build with AddressSanitizer" OFF)
|
|
34
|
+
# Compare the value, not just DEFINED: `MEW_ASAN=0 cmake …` must not enable it.
|
|
35
|
+
if(NOT MEW_ASAN AND DEFINED ENV{MEW_ASAN} AND "$ENV{MEW_ASAN}")
|
|
36
|
+
set(MEW_ASAN ON)
|
|
37
|
+
endif()
|
|
38
|
+
if(MEW_ASAN)
|
|
39
|
+
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
|
|
40
|
+
add_link_options(-fsanitize=address)
|
|
41
|
+
endif()
|
|
42
|
+
|
|
43
|
+
# ThreadSanitizer build, driven by -DMEW_TSAN=ON or MEW_TSAN=1 in the env.
|
|
44
|
+
# Mutually exclusive with ASAN — the two runtimes can't co-load.
|
|
45
|
+
option(MEW_TSAN "Build with ThreadSanitizer" OFF)
|
|
46
|
+
if(NOT MEW_TSAN AND DEFINED ENV{MEW_TSAN} AND "$ENV{MEW_TSAN}")
|
|
47
|
+
set(MEW_TSAN ON)
|
|
48
|
+
endif()
|
|
49
|
+
if(MEW_TSAN AND MEW_ASAN)
|
|
50
|
+
message(FATAL_ERROR "MEW_TSAN and MEW_ASAN cannot be enabled at the same time")
|
|
51
|
+
endif()
|
|
52
|
+
if(MEW_TSAN)
|
|
53
|
+
add_compile_options(-fsanitize=thread -fno-omit-frame-pointer)
|
|
54
|
+
add_link_options(-fsanitize=thread)
|
|
55
|
+
endif()
|
|
56
|
+
|
|
57
|
+
# --- Python + nanobind --------------------------------------------------------
|
|
58
|
+
# Optional, not required: a missing SABI module must degrade to a version-specific
|
|
59
|
+
# build rather than fail the configure, and this also works outside scikit-build-core,
|
|
60
|
+
# where SKBUILD_SABI_COMPONENT is undefined.
|
|
61
|
+
find_package(Python 3.11
|
|
62
|
+
REQUIRED COMPONENTS Interpreter Development.Module
|
|
63
|
+
OPTIONAL_COMPONENTS Development.SABIModule)
|
|
64
|
+
|
|
65
|
+
if(SKBUILD_SABI_COMPONENT AND NOT TARGET Python::SABIModule)
|
|
66
|
+
message(FATAL_ERROR
|
|
67
|
+
"scikit-build-core is building a stable-ABI wheel, but Python::SABIModule "
|
|
68
|
+
"was not found. Install the Python SABI development files, or clear "
|
|
69
|
+
"tool.scikit-build.wheel.py-api to build an untagged wheel.")
|
|
70
|
+
endif()
|
|
71
|
+
|
|
72
|
+
execute_process(
|
|
73
|
+
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
|
|
74
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
75
|
+
OUTPUT_VARIABLE nanobind_ROOT
|
|
76
|
+
)
|
|
77
|
+
find_package(nanobind CONFIG REQUIRED)
|
|
78
|
+
|
|
79
|
+
# --- Google Benchmark ---------------------------------------------------------
|
|
80
|
+
include(FetchContent)
|
|
81
|
+
|
|
82
|
+
set(MEW_BENCHMARK_COMMIT
|
|
83
|
+
"a8460680f0df91fd26205e0931708a26c3b4094d"
|
|
84
|
+
CACHE STRING "Google Benchmark commit SHA to fetch")
|
|
85
|
+
|
|
86
|
+
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
|
87
|
+
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE)
|
|
88
|
+
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
|
|
89
|
+
set(BENCHMARK_ENABLE_WERROR OFF CACHE BOOL "" FORCE)
|
|
90
|
+
|
|
91
|
+
FetchContent_Declare(
|
|
92
|
+
googlebenchmark
|
|
93
|
+
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
94
|
+
GIT_TAG ${MEW_BENCHMARK_COMMIT}
|
|
95
|
+
)
|
|
96
|
+
FetchContent_MakeAvailable(googlebenchmark)
|
|
97
|
+
|
|
98
|
+
# Report the commit FetchContent actually checked out, not just the requested
|
|
99
|
+
# tag. A stale or diverged `_deps` checkout (e.g. a CMake cache that kept an old
|
|
100
|
+
# pin) would otherwise make BENCHMARK_COMMIT lie about what was really built.
|
|
101
|
+
# Falls back to the requested pin when there is no `.git` (vendored sdist).
|
|
102
|
+
find_package(Git QUIET)
|
|
103
|
+
set(MEW_BENCHMARK_BUILT_COMMIT "${MEW_BENCHMARK_COMMIT}")
|
|
104
|
+
if(Git_FOUND AND IS_DIRECTORY "${googlebenchmark_SOURCE_DIR}/.git")
|
|
105
|
+
execute_process(
|
|
106
|
+
COMMAND "${GIT_EXECUTABLE}" -C "${googlebenchmark_SOURCE_DIR}" rev-parse HEAD
|
|
107
|
+
OUTPUT_VARIABLE MEW_BENCHMARK_BUILT_COMMIT
|
|
108
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
109
|
+
RESULT_VARIABLE _mew_rev_parse_status
|
|
110
|
+
ERROR_QUIET)
|
|
111
|
+
if(NOT _mew_rev_parse_status EQUAL 0)
|
|
112
|
+
set(MEW_BENCHMARK_BUILT_COMMIT "${MEW_BENCHMARK_COMMIT}")
|
|
113
|
+
endif()
|
|
114
|
+
endif()
|
|
115
|
+
|
|
116
|
+
message(STATUS "Google Benchmark requested: ${MEW_BENCHMARK_COMMIT}")
|
|
117
|
+
message(STATUS "Google Benchmark built: ${MEW_BENCHMARK_BUILT_COMMIT}")
|
|
118
|
+
|
|
119
|
+
# --- _core extension ----------------------------------------------------------
|
|
120
|
+
set(_MEW_SOURCES
|
|
121
|
+
src/_mew/module.cpp
|
|
122
|
+
src/_mew/state.cpp
|
|
123
|
+
src/_mew/registry.cpp
|
|
124
|
+
src/_mew/reporter.cpp
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# nanobind keeps whichever of STABLE_ABI / FREE_THREADED fits the interpreter:
|
|
128
|
+
# `cp312` abi3 on stock CPython, a `Py_MOD_GIL_NOT_USED` wheel on free-threaded
|
|
129
|
+
# builds (the limited API has no free-threaded variant). Inert on a GIL build.
|
|
130
|
+
nanobind_add_module(
|
|
131
|
+
_core
|
|
132
|
+
STABLE_ABI
|
|
133
|
+
FREE_THREADED
|
|
134
|
+
NB_STATIC
|
|
135
|
+
${_MEW_SOURCES}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
target_include_directories(_core PRIVATE src/_mew)
|
|
139
|
+
target_link_libraries(_core PRIVATE benchmark::benchmark)
|
|
140
|
+
target_compile_definitions(_core PRIVATE
|
|
141
|
+
MEW_BENCHMARK_COMMIT="${MEW_BENCHMARK_BUILT_COMMIT}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Repeat the sanitizer flag on _core itself: nanobind's stubgen detector only
|
|
145
|
+
# scans per-target COMPILE_OPTIONS / LINK_OPTIONS. Without this it skips
|
|
146
|
+
# preloading libclang_rt.asan and stubgen aborts on `import _core`.
|
|
147
|
+
if(MEW_ASAN)
|
|
148
|
+
target_compile_options(_core PRIVATE -fsanitize=address -fno-omit-frame-pointer)
|
|
149
|
+
target_link_options(_core PRIVATE -fsanitize=address)
|
|
150
|
+
endif()
|
|
151
|
+
if(MEW_TSAN)
|
|
152
|
+
target_compile_options(_core PRIVATE -fsanitize=thread -fno-omit-frame-pointer)
|
|
153
|
+
target_link_options(_core PRIVATE -fsanitize=thread)
|
|
154
|
+
endif()
|
|
155
|
+
|
|
156
|
+
install(TARGETS _core LIBRARY DESTINATION mew)
|
|
157
|
+
|
|
158
|
+
# Emit the .pyi stub next to the Python sources so IDEs and type checkers
|
|
159
|
+
# pick it up via the src/-layout (which shadows site-packages for `mew._core`).
|
|
160
|
+
nanobind_add_stub(
|
|
161
|
+
_core_stub
|
|
162
|
+
MODULE _core
|
|
163
|
+
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/mew/_core.pyi
|
|
164
|
+
PATTERN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/_mew/stubgen.pat
|
|
165
|
+
PYTHON_PATH $<TARGET_FILE_DIR:_core>
|
|
166
|
+
DEPENDS _core
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Run `ruff format` on the freshly-generated stub. Skipped if ruff isn't
|
|
170
|
+
# available (e.g. building from an sdist without dev extras).
|
|
171
|
+
execute_process(
|
|
172
|
+
COMMAND ${Python_EXECUTABLE} -m ruff --version
|
|
173
|
+
RESULT_VARIABLE _ruff_available
|
|
174
|
+
OUTPUT_QUIET ERROR_QUIET
|
|
175
|
+
)
|
|
176
|
+
if(_ruff_available EQUAL 0)
|
|
177
|
+
add_custom_command(
|
|
178
|
+
TARGET _core_stub POST_BUILD
|
|
179
|
+
COMMAND ${Python_EXECUTABLE} -m ruff check --fix --exit-zero --quiet
|
|
180
|
+
${CMAKE_CURRENT_SOURCE_DIR}/src/mew/_core.pyi
|
|
181
|
+
COMMAND ${Python_EXECUTABLE} -m ruff format --quiet
|
|
182
|
+
${CMAKE_CURRENT_SOURCE_DIR}/src/mew/_core.pyi
|
|
183
|
+
COMMENT "ruff fix + format _core.pyi"
|
|
184
|
+
VERBATIM
|
|
185
|
+
)
|
|
186
|
+
endif()
|