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.
Files changed (115) hide show
  1. mew_bench-0.1.0/.clang-format +3 -0
  2. mew_bench-0.1.0/.clangd +16 -0
  3. mew_bench-0.1.0/.dockerignore +18 -0
  4. mew_bench-0.1.0/.github/workflows/benchmark.yml.sketch +98 -0
  5. mew_bench-0.1.0/.github/workflows/ci.yml +117 -0
  6. mew_bench-0.1.0/.github/workflows/release.yml +86 -0
  7. mew_bench-0.1.0/.gitignore +44 -0
  8. mew_bench-0.1.0/.pre-commit-config.yaml +28 -0
  9. mew_bench-0.1.0/.readthedocs.yaml +23 -0
  10. mew_bench-0.1.0/CLAUDE.md +27 -0
  11. mew_bench-0.1.0/CMakeLists.txt +186 -0
  12. mew_bench-0.1.0/LICENSE +201 -0
  13. mew_bench-0.1.0/PKG-INFO +87 -0
  14. mew_bench-0.1.0/README.md +50 -0
  15. mew_bench-0.1.0/ROADMAP.md +326 -0
  16. mew_bench-0.1.0/benchmarks/bench_batched.py +22 -0
  17. mew_bench-0.1.0/benchmarks/bench_cpu.py +64 -0
  18. mew_bench-0.1.0/benchmarks/bench_memory.py +27 -0
  19. mew_bench-0.1.0/benchmarks/bench_threads.py +66 -0
  20. mew_bench-0.1.0/docker/profile.Dockerfile +42 -0
  21. mew_bench-0.1.0/docs/_static/.keep +0 -0
  22. mew_bench-0.1.0/docs/changelog.md +71 -0
  23. mew_bench-0.1.0/docs/conf.py +148 -0
  24. mew_bench-0.1.0/docs/development/building.md +142 -0
  25. mew_bench-0.1.0/docs/development/contributing.md +72 -0
  26. mew_bench-0.1.0/docs/getting-started/concepts.md +65 -0
  27. mew_bench-0.1.0/docs/getting-started/installation.md +97 -0
  28. mew_bench-0.1.0/docs/getting-started/quickstart.md +67 -0
  29. mew_bench-0.1.0/docs/guide/cli.md +181 -0
  30. mew_bench-0.1.0/docs/guide/configuration.md +47 -0
  31. mew_bench-0.1.0/docs/guide/context.md +66 -0
  32. mew_bench-0.1.0/docs/guide/parametrize-product.md +92 -0
  33. mew_bench-0.1.0/docs/guide/profiling-cpu.md +52 -0
  34. mew_bench-0.1.0/docs/guide/profiling-memory.md +45 -0
  35. mew_bench-0.1.0/docs/guide/profiling-native.md +187 -0
  36. mew_bench-0.1.0/docs/guide/regressions.md +131 -0
  37. mew_bench-0.1.0/docs/guide/reporters.md +117 -0
  38. mew_bench-0.1.0/docs/guide/state-and-timing.md +125 -0
  39. mew_bench-0.1.0/docs/guide/trusting-results.md +132 -0
  40. mew_bench-0.1.0/docs/guide/variants.md +106 -0
  41. mew_bench-0.1.0/docs/guide/writing-benchmarks.md +86 -0
  42. mew_bench-0.1.0/docs/index.md +113 -0
  43. mew_bench-0.1.0/docs/reference/api/compare.md +6 -0
  44. mew_bench-0.1.0/docs/reference/api/config.md +6 -0
  45. mew_bench-0.1.0/docs/reference/api/context.md +10 -0
  46. mew_bench-0.1.0/docs/reference/api/decorators.md +16 -0
  47. mew_bench-0.1.0/docs/reference/api/index.md +50 -0
  48. mew_bench-0.1.0/docs/reference/api/profiling.md +13 -0
  49. mew_bench-0.1.0/docs/reference/api/registry.md +18 -0
  50. mew_bench-0.1.0/docs/reference/api/regressions.md +6 -0
  51. mew_bench-0.1.0/docs/reference/api/reporters.md +23 -0
  52. mew_bench-0.1.0/docs/reference/api/runner.md +15 -0
  53. mew_bench-0.1.0/docs/reference/api/state.md +13 -0
  54. mew_bench-0.1.0/docs/reference/cli.md +46 -0
  55. mew_bench-0.1.0/pyproject.toml +162 -0
  56. mew_bench-0.1.0/scripts/asan-pytest.sh +38 -0
  57. mew_bench-0.1.0/scripts/tsan-pytest.sh +41 -0
  58. mew_bench-0.1.0/src/_mew/interrupt.h +17 -0
  59. mew_bench-0.1.0/src/_mew/module.cpp +27 -0
  60. mew_bench-0.1.0/src/_mew/registry.cpp +125 -0
  61. mew_bench-0.1.0/src/_mew/reporter.cpp +234 -0
  62. mew_bench-0.1.0/src/_mew/state.cpp +162 -0
  63. mew_bench-0.1.0/src/_mew/stubgen.pat +5 -0
  64. mew_bench-0.1.0/src/mew/__init__.py +54 -0
  65. mew_bench-0.1.0/src/mew/_completion_cache.py +96 -0
  66. mew_bench-0.1.0/src/mew/_completions.py +291 -0
  67. mew_bench-0.1.0/src/mew/_console.py +186 -0
  68. mew_bench-0.1.0/src/mew/_core.pyi +333 -0
  69. mew_bench-0.1.0/src/mew/_discovery.py +129 -0
  70. mew_bench-0.1.0/src/mew/_profile.py +208 -0
  71. mew_bench-0.1.0/src/mew/_registry.py +192 -0
  72. mew_bench-0.1.0/src/mew/_session.py +86 -0
  73. mew_bench-0.1.0/src/mew/_statistics.py +109 -0
  74. mew_bench-0.1.0/src/mew/_subprocess_worker.py +57 -0
  75. mew_bench-0.1.0/src/mew/_typing.py +138 -0
  76. mew_bench-0.1.0/src/mew/_variant_worker.py +115 -0
  77. mew_bench-0.1.0/src/mew/_variants.py +281 -0
  78. mew_bench-0.1.0/src/mew/api.py +484 -0
  79. mew_bench-0.1.0/src/mew/cli.py +1135 -0
  80. mew_bench-0.1.0/src/mew/compare.py +978 -0
  81. mew_bench-0.1.0/src/mew/config.py +142 -0
  82. mew_bench-0.1.0/src/mew/context.py +116 -0
  83. mew_bench-0.1.0/src/mew/cpu.py +205 -0
  84. mew_bench-0.1.0/src/mew/memory.py +211 -0
  85. mew_bench-0.1.0/src/mew/profilers/__init__.py +62 -0
  86. mew_bench-0.1.0/src/mew/profilers/_xctrace_export.py +215 -0
  87. mew_bench-0.1.0/src/mew/profilers/base.py +110 -0
  88. mew_bench-0.1.0/src/mew/profilers/perf.py +113 -0
  89. mew_bench-0.1.0/src/mew/profilers/pyspy.py +83 -0
  90. mew_bench-0.1.0/src/mew/profilers/xctrace.py +155 -0
  91. mew_bench-0.1.0/src/mew/py.typed +0 -0
  92. mew_bench-0.1.0/src/mew/regressions.py +278 -0
  93. mew_bench-0.1.0/src/mew/reporter.py +469 -0
  94. mew_bench-0.1.0/src/mew/runner.py +355 -0
  95. mew_bench-0.1.0/tests/_helpers.py +101 -0
  96. mew_bench-0.1.0/tests/conftest.py +17 -0
  97. mew_bench-0.1.0/tests/smoke_wheel.py +40 -0
  98. mew_bench-0.1.0/tests/test_api.py +426 -0
  99. mew_bench-0.1.0/tests/test_cli.py +840 -0
  100. mew_bench-0.1.0/tests/test_compare.py +943 -0
  101. mew_bench-0.1.0/tests/test_config.py +109 -0
  102. mew_bench-0.1.0/tests/test_console.py +116 -0
  103. mew_bench-0.1.0/tests/test_context.py +191 -0
  104. mew_bench-0.1.0/tests/test_discovery.py +146 -0
  105. mew_bench-0.1.0/tests/test_profile.py +475 -0
  106. mew_bench-0.1.0/tests/test_profilers.py +409 -0
  107. mew_bench-0.1.0/tests/test_profilers_native.py +83 -0
  108. mew_bench-0.1.0/tests/test_registry.py +172 -0
  109. mew_bench-0.1.0/tests/test_regressions.py +293 -0
  110. mew_bench-0.1.0/tests/test_reporter.py +348 -0
  111. mew_bench-0.1.0/tests/test_runner.py +454 -0
  112. mew_bench-0.1.0/tests/test_session.py +239 -0
  113. mew_bench-0.1.0/tests/test_variants.py +343 -0
  114. mew_bench-0.1.0/tests/test_xctrace_export.py +95 -0
  115. mew_bench-0.1.0/uv.lock +1183 -0
@@ -0,0 +1,3 @@
1
+ BasedOnStyle: Google
2
+ ColumnLimit: 100
3
+ IndentWidth: 4
@@ -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()