benchset 0.0.2__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.
- benchset-0.0.2/Cargo.lock +7 -0
- benchset-0.0.2/Cargo.toml +25 -0
- benchset-0.0.2/LICENSE +21 -0
- benchset-0.0.2/MANIFEST.in +12 -0
- benchset-0.0.2/PKG-INFO +437 -0
- benchset-0.0.2/Package.swift +17 -0
- benchset-0.0.2/README.md +403 -0
- benchset-0.0.2/Sources/Benchset/Benchset.swift +149 -0
- benchset-0.0.2/benchset/__init__.py +16 -0
- benchset-0.0.2/benchset/__main__.py +3 -0
- benchset-0.0.2/benchset/_native.py +203 -0
- benchset-0.0.2/benchset/api.py +134 -0
- benchset-0.0.2/benchset/cli.py +168 -0
- benchset-0.0.2/benchset/models.py +52 -0
- benchset-0.0.2/benchset/py.typed +0 -0
- benchset-0.0.2/benchset.egg-info/PKG-INFO +437 -0
- benchset-0.0.2/benchset.egg-info/SOURCES.txt +48 -0
- benchset-0.0.2/benchset.egg-info/dependency_links.txt +1 -0
- benchset-0.0.2/benchset.egg-info/entry_points.txt +2 -0
- benchset-0.0.2/benchset.egg-info/requires.txt +1 -0
- benchset-0.0.2/benchset.egg-info/top_level.txt +1 -0
- benchset-0.0.2/examples/native/c.c +26 -0
- benchset-0.0.2/examples/native/rust.rs +15 -0
- benchset-0.0.2/examples/native/swift.swift +5 -0
- benchset-0.0.2/include/benchset.h +64 -0
- benchset-0.0.2/include/module.modulemap +4 -0
- benchset-0.0.2/native/src/ffi.rs +302 -0
- benchset-0.0.2/native/src/ffi_tests.rs +411 -0
- benchset-0.0.2/native/src/kernel.rs +113 -0
- benchset-0.0.2/native/src/kernel_tests.rs +87 -0
- benchset-0.0.2/native/src/lib.rs +555 -0
- benchset-0.0.2/native/src/runtime_tests.rs +638 -0
- benchset-0.0.2/pyproject.toml +80 -0
- benchset-0.0.2/rust-toolchain.toml +3 -0
- benchset-0.0.2/scripts/__init__.py +1 -0
- benchset-0.0.2/scripts/build_backend.py +107 -0
- benchset-0.0.2/scripts/build_native.py +17 -0
- benchset-0.0.2/scripts/check_native_coverage.py +126 -0
- benchset-0.0.2/scripts/check_source_install.py +44 -0
- benchset-0.0.2/scripts/package_native.py +78 -0
- benchset-0.0.2/scripts/release.py +369 -0
- benchset-0.0.2/setup.cfg +4 -0
- benchset-0.0.2/setup.py +83 -0
- benchset-0.0.2/tests/swift/BenchsetTests.swift +413 -0
- benchset-0.0.2/tests/test_benchset.py +189 -0
- benchset-0.0.2/tests/test_build.py +223 -0
- benchset-0.0.2/tests/test_errors.py +195 -0
- benchset-0.0.2/tests/test_release.py +237 -0
- benchset-0.0.2/tests/test_resources.py +114 -0
- benchset-0.0.2/tests/test_semantics.py +552 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "benchset"
|
|
3
|
+
version = "0.0.2"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
rust-version = "1.74"
|
|
6
|
+
description = "Embeddable sustained scalar CPU benchmarks with a C ABI"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
repository = "https://github.com/mutating/benchset"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = ["benchmark", "cpu", "ffi", "performance"]
|
|
11
|
+
categories = ["development-tools::profiling"]
|
|
12
|
+
include = ["/Cargo.toml", "/Cargo.lock", "/README.md", "/LICENSE", "/native/**", "/include/**", "/examples/native/**"]
|
|
13
|
+
|
|
14
|
+
[lib]
|
|
15
|
+
path = "native/src/lib.rs"
|
|
16
|
+
crate-type = ["rlib", "staticlib", "cdylib"]
|
|
17
|
+
|
|
18
|
+
[profile.release]
|
|
19
|
+
opt-level = 3
|
|
20
|
+
lto = "thin"
|
|
21
|
+
codegen-units = 1
|
|
22
|
+
|
|
23
|
+
[[example]]
|
|
24
|
+
name = "rust"
|
|
25
|
+
path = "examples/native/rust.rs"
|
benchset-0.0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 The Mutating Company
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
include setup.py
|
|
2
|
+
include Cargo.toml Cargo.lock rust-toolchain.toml LICENSE README.md
|
|
3
|
+
recursive-include native *.rs
|
|
4
|
+
recursive-include include *.h *.modulemap
|
|
5
|
+
recursive-include scripts *.py
|
|
6
|
+
recursive-include examples *.c *.rs *.swift
|
|
7
|
+
include Package.swift
|
|
8
|
+
recursive-include Sources *.swift
|
|
9
|
+
recursive-include tests *.swift
|
|
10
|
+
prune target
|
|
11
|
+
prune Artifacts
|
|
12
|
+
prune benchset/_native
|
benchset-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: benchset
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Sustained scalar CPU benchmarks with an embeddable Rust core
|
|
5
|
+
Author-email: Evgeniy Blinov <zheni-b@yandex.ru>
|
|
6
|
+
Project-URL: Source, https://github.com/mutating/benchset
|
|
7
|
+
Project-URL: Tracker, https://github.com/mutating/benchset/issues
|
|
8
|
+
Keywords: benchmarks
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
11
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
12
|
+
Classifier: Operating System :: POSIX
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
|
+
Classifier: Programming Language :: Python :: Free Threading
|
|
24
|
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
|
25
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
26
|
+
Classifier: Intended Audience :: Developers
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.8
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: skelet>=0.0.21
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
Sustained scalar CPU throughput, with an embeddable Rust core, a public C ABI,
|
|
38
|
+
a Python library, a CLI, and a macOS Swift package. No Python interpreter is
|
|
39
|
+
needed by applications embedding the native library.
|
|
40
|
+
|
|
41
|
+
The initial suite contains **`single_core`** (one computational worker) and
|
|
42
|
+
**`multi_core`** (all available computational workers). Both execute the same
|
|
43
|
+
fixed mixed workload and return a score in **completed iterations per second**.
|
|
44
|
+
Higher is faster. There is no reference machine or score normalization.
|
|
45
|
+
|
|
46
|
+
## Install and run
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
pip install benchset
|
|
50
|
+
benchset
|
|
51
|
+
benchset --bench single_core
|
|
52
|
+
benchset --bench multi_core --warmup 120 --duration 60
|
|
53
|
+
python -m benchset --warmup 0 --duration 1
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The last command is a smoke test, not a sustained performance measurement.
|
|
57
|
+
Supported wheels include the native library: Rust is not required at runtime
|
|
58
|
+
or to install a wheel. `pip install .` and source distributions build the Rust
|
|
59
|
+
core automatically. If Cargo is not on PATH, the build downloads the pinned Rust
|
|
60
|
+
version with [rustup](https://rust-lang.github.io/rustup/installation/other.html)
|
|
61
|
+
into a private user cache and reuses it on later builds. No shell profile or
|
|
62
|
+
system PATH changes are made, and no manual environment setup is required.
|
|
63
|
+
The first such build needs internet access. Source compilation still requires
|
|
64
|
+
the platform linker/SDK: macOS Command Line Tools, a Linux C toolchain, or
|
|
65
|
+
Windows MSVC Build Tools with the Windows SDK. Prebuilt wheels need none of
|
|
66
|
+
these build tools.
|
|
67
|
+
|
|
68
|
+
| CLI option | Default | Meaning |
|
|
69
|
+
|---|---:|---|
|
|
70
|
+
| `--bench` | all | `single_core` or `multi_core`; omitted runs both in this order |
|
|
71
|
+
| `--warmup` | 120 | Seconds of continuous workload excluded from the score; zero is allowed |
|
|
72
|
+
| `--duration` | 60 | Seconds of measurement after warmup; must be positive |
|
|
73
|
+
| `--help` | | Show syntax |
|
|
74
|
+
| `--version` | | Show the library/benchmark version |
|
|
75
|
+
|
|
76
|
+
`--bench` is the public spelling of the selector. CLI settings are read through
|
|
77
|
+
`skelet`; configuration files and environment settings are not used.
|
|
78
|
+
|
|
79
|
+
A default full suite takes approximately six minutes. Each benchmark has its
|
|
80
|
+
own warmup. Warmup transitions directly into measurement without stopping the
|
|
81
|
+
workload. Terminal progress is shown on stdout only when attached to a TTY.
|
|
82
|
+
Completed result fields, including all measurement samples, are printed to
|
|
83
|
+
stdout. Failed/cancelled results go to stderr. Exit codes: 0 completed, 1
|
|
84
|
+
execution failure, 2 invalid CLI arguments, 130 interrupted/cancelled. The
|
|
85
|
+
suite stops after a failed or cancelled benchmark.
|
|
86
|
+
|
|
87
|
+
## Python API
|
|
88
|
+
|
|
89
|
+
Python 3.8–3.15, including free-threaded CPython, is supported. The wrapper uses
|
|
90
|
+
standard-library `ctypes` and loads the version-matched bundled native library.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from benchset import run, run_all
|
|
94
|
+
|
|
95
|
+
result = run('single_core', warmup_seconds=120, measurement_seconds=60)
|
|
96
|
+
if result.success:
|
|
97
|
+
print(result.score, result.unit)
|
|
98
|
+
else:
|
|
99
|
+
print(result.status, result.reason)
|
|
100
|
+
|
|
101
|
+
results = run_all(warmup_seconds=120, measurement_seconds=60)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For a GUI or cancellable task, run a reusable runner on a background thread:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
108
|
+
from benchset import BenchmarkRunner
|
|
109
|
+
|
|
110
|
+
runner = BenchmarkRunner()
|
|
111
|
+
with ThreadPoolExecutor(max_workers=1) as pool:
|
|
112
|
+
future = pool.submit(
|
|
113
|
+
runner.run, 'multi_core', warmup_seconds=120, measurement_seconds=60,
|
|
114
|
+
on_progress=lambda p: print(p.phase, p.fraction),
|
|
115
|
+
)
|
|
116
|
+
# A UI handler on another thread may call runner.cancel().
|
|
117
|
+
result = future.result()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Callbacks execute on the thread calling `run`, approximately ten times per
|
|
121
|
+
second and on observed phase transitions. A very short phase can finish before
|
|
122
|
+
the coordinator reports it, so callback sequences may omit that phase.
|
|
123
|
+
Callbacks receive the benchmark name, phase,
|
|
124
|
+
worker count, overall fraction, phase elapsed/target time and completed
|
|
125
|
+
iterations in that phase. Dispatch UI updates to the UI thread yourself and
|
|
126
|
+
return promptly. A Python callback exception cancels the workload and produces
|
|
127
|
+
a failed result; it does not propagate through C.
|
|
128
|
+
|
|
129
|
+
`BenchmarkRunner.cancel()` is thread-safe while a call is active. After it
|
|
130
|
+
returns, all native workers are joined and the runner can be reused. Concurrent
|
|
131
|
+
runs are rejected. The native core enforces one active run per loaded library
|
|
132
|
+
instance, including calls from separate Python runners. Separate OS processes
|
|
133
|
+
or separately linked copies of the library can still compete for CPU time.
|
|
134
|
+
|
|
135
|
+
## What one iteration does
|
|
136
|
+
|
|
137
|
+
This is a fixed scalar integer workload, not a prediction of arbitrary
|
|
138
|
+
application execution time. Every complete iteration consists of:
|
|
139
|
+
|
|
140
|
+
1. **Integer arithmetic:** 256 dependent rounds of wrapping 64-bit addition,
|
|
141
|
+
multiplication, XOR, shifts and rotation.
|
|
142
|
+
2. **Stateful parsing:** a pass over 4,096 bytes of deterministic structured
|
|
143
|
+
input, using data-dependent transitions, digit accumulation, punctuation,
|
|
144
|
+
signs, exponents and invalid-token handling.
|
|
145
|
+
3. **Indexed data access:** 1,024 dependent indexed reads and modifications of
|
|
146
|
+
a 32,768-element `u64` table (256 KiB). The next index depends on the current
|
|
147
|
+
data and accumulator.
|
|
148
|
+
|
|
149
|
+
The table and parser input belong to each worker. Initialization and allocation
|
|
150
|
+
occur before warmup; initialization time is excluded. The initial worker seeds
|
|
151
|
+
are fixed and distinct. State continues evolving during warmup and measurement.
|
|
152
|
+
Checksums make the computed values observable. The implementation uses explicit
|
|
153
|
+
wrapping operations so overflow has identical semantics across builds.
|
|
154
|
+
|
|
155
|
+
There is one fixed package of each kind per iteration. These are **not equal
|
|
156
|
+
percentages of elapsed time or CPU instructions**. The recipe and data sizes
|
|
157
|
+
remain identical across machines. Single-core uses one worker; multi-core sums
|
|
158
|
+
complete iterations from independent workers without shared data locks in the
|
|
159
|
+
measured kernel. Multicore throughput is measured, not extrapolated from the
|
|
160
|
+
single-core score.
|
|
161
|
+
|
|
162
|
+
Normal release compiler optimization is enabled. There is no `target-cpu=native`,
|
|
163
|
+
inline assembly, manual SIMD, hardware cryptography or CPU-specific dispatch.
|
|
164
|
+
The dependent operations prevent the main loops from becoming independent
|
|
165
|
+
vector workloads. Floating-point, SIMD, GPU, disk and network benchmarks are
|
|
166
|
+
outside this initial suite. Memory capacity is not measured.
|
|
167
|
+
|
|
168
|
+
## Timing and result interpretation
|
|
169
|
+
|
|
170
|
+
The default is 120 seconds of continuous warmup followed by 60 seconds of
|
|
171
|
+
measurement. Both durations are configurable in Rust, C, Swift, Python and CLI.
|
|
172
|
+
This is a starting point for sustained measurements; it cannot guarantee
|
|
173
|
+
thermal equilibrium on every device. Stable turbo operation is part of the
|
|
174
|
+
measured performance. The library does not change power settings, priorities,
|
|
175
|
+
CPU affinity, background activity or cooling conditions.
|
|
176
|
+
|
|
177
|
+
`multi_core` queries Rust's [`available_parallelism`](https://doc.rust-lang.org/std/thread/fn.available_parallelism.html).
|
|
178
|
+
The returned count is recorded in the result. This portable API is an estimate
|
|
179
|
+
of process-available parallelism; its documented limitations include Windows
|
|
180
|
+
processor groups above 64 logical CPUs and some affinity/quota configurations.
|
|
181
|
+
The library does not reserve cores or model sharing between isolates.
|
|
182
|
+
|
|
183
|
+
The primary score is `iterations / elapsed_seconds`, using monotonic wall time
|
|
184
|
+
after warmup. Workers publish counters in batches of eight iterations; a final
|
|
185
|
+
batch can slightly extend the requested interval. The actual measured duration
|
|
186
|
+
is included and used as the denominator. Worker setup and teardown are excluded.
|
|
187
|
+
Cancellation is checked between batches; completion waits for active workers.
|
|
188
|
+
|
|
189
|
+
The immutable Python result contains:
|
|
190
|
+
|
|
191
|
+
| Field | Meaning |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `benchmark`, `version` | Name and benchmark/library version |
|
|
194
|
+
| `status`, `success`, `reason` | `completed`, `cancelled` or `failed`; explanation on failure |
|
|
195
|
+
| `score`, `unit` | Iterations/s; `score=None` unless completed |
|
|
196
|
+
| `workers` | Number of computational workers |
|
|
197
|
+
| `warmup_seconds`, `measurement_seconds` | Requested windows |
|
|
198
|
+
| `elapsed_seconds`, `iterations` | Actual measured wall time and completed work |
|
|
199
|
+
| `checksum` | XOR of final per-worker checksums; diagnostic, not a score |
|
|
200
|
+
| `samples` | Approximately one-second intervals plus the final partial interval |
|
|
201
|
+
| `variation_percent`, `unstable` | Time-weighted coefficient of variation, and whether it exceeds 10% |
|
|
202
|
+
|
|
203
|
+
Each sample stores cumulative measured elapsed time, interval duration,
|
|
204
|
+
interval iterations and iterations/s. Variation is `None` with fewer than two
|
|
205
|
+
samples. It reports within-run variability, not repeat-to-repeat accuracy.
|
|
206
|
+
An unstable run retains its score and completed status; the consumer decides
|
|
207
|
+
whether to use it. Cancellation/failure can retain partial diagnostics but
|
|
208
|
+
never produces a usable score.
|
|
209
|
+
|
|
210
|
+
The Python package version is defined in `pyproject.toml`. `benchset.__version__`
|
|
211
|
+
and result objects read the installed package metadata through
|
|
212
|
+
`importlib.metadata`; no Python source version constant needs updating.
|
|
213
|
+
The native library shares this version; builds check its Cargo metadata for
|
|
214
|
+
consistency. Consumers decide which versions and measurement settings are comparable.
|
|
215
|
+
|
|
216
|
+
## Embed in Rust
|
|
217
|
+
|
|
218
|
+
```toml
|
|
219
|
+
[dependencies]
|
|
220
|
+
benchset = "0.0.1"
|
|
221
|
+
# Before registry publication, use:
|
|
222
|
+
# benchset = { git = "https://github.com/mutating/benchset.git" }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```rust
|
|
226
|
+
use benchset::{run, Benchmark, Cancellation, Options};
|
|
227
|
+
let cancellation = Cancellation::default();
|
|
228
|
+
let result = run(Benchmark::MultiCore, Options::default(), &cancellation,
|
|
229
|
+
|progress| println!("{:?}: {}", progress.phase, progress.fraction));
|
|
230
|
+
println!("{:?}", result.score);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The cancellation token can be cloned and cancelled from another thread. It is
|
|
234
|
+
sticky: create a new token for a new call. A short runnable example is in
|
|
235
|
+
`examples/native/rust.rs`.
|
|
236
|
+
|
|
237
|
+
## Embed through C ABI
|
|
238
|
+
|
|
239
|
+
Download the archive for the target OS/architecture from GitHub Releases, or
|
|
240
|
+
build locally:
|
|
241
|
+
|
|
242
|
+
```sh
|
|
243
|
+
cargo build --release --locked
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Artifacts include `.a`/`.lib` for static linking and `.dylib`/`.so`/`.dll` for
|
|
247
|
+
dynamic loading. `include/benchset.h` documents structures, status codes and
|
|
248
|
+
lifetime rules. `examples/native/c.c` demonstrates configuration and progress.
|
|
249
|
+
|
|
250
|
+
```c
|
|
251
|
+
BenchsetConfig config;
|
|
252
|
+
benchset_default_config(&config);
|
|
253
|
+
config.benchmark = BENCHSET_MULTI_CORE;
|
|
254
|
+
BenchsetHandle *handle = benchset_create();
|
|
255
|
+
if (handle) {
|
|
256
|
+
benchset_run(handle, &config, callback, context);
|
|
257
|
+
BenchsetResult result;
|
|
258
|
+
if (benchset_result(handle, &result) && result.status == BENCHSET_COMPLETED) {
|
|
259
|
+
/* result.score is iterations/s */
|
|
260
|
+
}
|
|
261
|
+
benchset_free(handle);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Call `benchset_cancel(handle)` from another thread or a progress callback.
|
|
266
|
+
Handles are single-use. Getters are used after `benchset_run` returns. Strings
|
|
267
|
+
are borrowed until the handle is freed; progress pointers are borrowed only
|
|
268
|
+
during the callback. Do not free a handle while it is in use or throw/unwind
|
|
269
|
+
through a C callback. Invalid configuration produces a failed result.
|
|
270
|
+
|
|
271
|
+
When statically linking Rust into a foreign application, system libraries may
|
|
272
|
+
also be required. Obtain their list with
|
|
273
|
+
`cargo rustc --release -- --print=native-static-libs`. Dynamic consumers must
|
|
274
|
+
ship the matching platform library with their application. Source consumers
|
|
275
|
+
control their compiler/toolchain settings; use our release artifacts for a
|
|
276
|
+
consistent build policy.
|
|
277
|
+
|
|
278
|
+
## Embed in Swift on macOS
|
|
279
|
+
|
|
280
|
+
In Xcode, add `https://github.com/mutating/benchset.git` as a package dependency
|
|
281
|
+
and select a released version. Release tags contain a `Package.swift` that
|
|
282
|
+
downloads the universal ARM64/Intel static XCFramework from that version's
|
|
283
|
+
GitHub Release and verifies its SHA-256 checksum. The Swift wrapper lives in
|
|
284
|
+
the same repository. Rust is not required by the consumer.
|
|
285
|
+
|
|
286
|
+
For a Swift manifest, add:
|
|
287
|
+
|
|
288
|
+
```swift
|
|
289
|
+
.package(url: "https://github.com/mutating/benchset.git", .exact("0.0.1"))
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Add `.product(name: "Benchset", package: "benchset")` to your target's
|
|
293
|
+
dependencies. Pinning a version also pins the benchmark implementation.
|
|
294
|
+
|
|
295
|
+
For offline use, download and extract **`Benchset-Swift.zip`** from the release
|
|
296
|
+
and add the extracted directory as a local package. It contains the wrapper,
|
|
297
|
+
local manifest and XCFramework. Development branches use the local artifact
|
|
298
|
+
path and need the build step below; release tags use the remote binary target.
|
|
299
|
+
|
|
300
|
+
```swift
|
|
301
|
+
import Benchset
|
|
302
|
+
let runner = BenchmarkRunner()
|
|
303
|
+
// Execute from a background queue; dispatch progress updates to the UI queue.
|
|
304
|
+
let result = try runner.run(.multiCore, warmupSeconds: 120, measurementSeconds: 60) {
|
|
305
|
+
progress in print(progress.fraction)
|
|
306
|
+
}
|
|
307
|
+
// Another thread may call runner.cancel() while run is active.
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
To build the Apple package from source:
|
|
311
|
+
|
|
312
|
+
```sh
|
|
313
|
+
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
|
314
|
+
python scripts/package_native.py --target aarch64-apple-darwin --apple
|
|
315
|
+
swift build
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
This requires a macOS SDK and `lipo`. The packager generates the XCFramework
|
|
319
|
+
with standard bundle metadata and C headers; no Xcode project is required.
|
|
320
|
+
The Swift test suite uses Swift Testing (Swift 6 or newer). Run it with
|
|
321
|
+
`python scripts/check_native_coverage.py swift`; it also works with current
|
|
322
|
+
Command Line Tools, without full Xcode. The test executable targets macOS 13
|
|
323
|
+
while the library keeps the deployment target declared in `Package.swift`.
|
|
324
|
+
|
|
325
|
+
## Platforms and development
|
|
326
|
+
|
|
327
|
+
Release builds target macOS ARM64/Intel, Linux x86-64/ARM64 with glibc, and
|
|
328
|
+
Windows x86-64 MSVC. Native baseline targets are macOS 11 ARM64 / 10.12 Intel,
|
|
329
|
+
Linux glibc 2.17 and Windows 10, following ordinary Rust target requirements.
|
|
330
|
+
Older-OS execution still needs platform testing; the installed Python version
|
|
331
|
+
or a host application may impose a newer minimum. Linux musl and Windows ARM64
|
|
332
|
+
are not part of the initial prebuilt matrix.
|
|
333
|
+
|
|
334
|
+
```sh
|
|
335
|
+
python -m venv .venv
|
|
336
|
+
# Activate the virtual environment using your shell's normal command.
|
|
337
|
+
pip install -r requirements_dev.txt 'skelet>=0.0.21' wheel setuptools
|
|
338
|
+
pip install .
|
|
339
|
+
python scripts/build_native.py
|
|
340
|
+
python -m pytest
|
|
341
|
+
cargo test -- --test-threads=1
|
|
342
|
+
python -m build
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`scripts/build_native.py` makes the source Python package usable without an
|
|
346
|
+
installation. Release wheels are platform-specific but interpreter-independent
|
|
347
|
+
(`py3-none-<platform>`). The sdist includes the native source. Tests validate
|
|
348
|
+
correctness, result accounting, progress, cancellation and API/CLI behavior;
|
|
349
|
+
they do not enforce speed thresholds in CI.
|
|
350
|
+
|
|
351
|
+
## Test coverage
|
|
352
|
+
|
|
353
|
+
The [test matrix](tests/coverage-plan.md) combines five independent reviews and
|
|
354
|
+
covers measurement accounting, statistical boundaries, cancellation, reentry,
|
|
355
|
+
callback failures, C ABI validation, ownership and CLI behavior. Tests use
|
|
356
|
+
controlled initialization failures instead of exhausting machine resources.
|
|
357
|
+
|
|
358
|
+
Python coverage includes statements and branches, including CLI subprocesses;
|
|
359
|
+
CI installs a coverage startup hook for those subprocesses. Rust and Swift each
|
|
360
|
+
have independent 100% line/function/region/instantiation gates. Their current
|
|
361
|
+
LLVM reports do not provide branch counters, so no separate native branch
|
|
362
|
+
percentage is claimed. Only benchmark runtime and wrappers are counted;
|
|
363
|
+
build/release utilities and tests are outside those coverage percentages.
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
rustup component add llvm-tools-preview
|
|
367
|
+
python scripts/check_native_coverage.py rust
|
|
368
|
+
python scripts/check_native_coverage.py swift
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Build the local XCFramework first as described above before the Swift command.
|
|
372
|
+
The checker uses the pinned Rust toolchain's LLVM tools; `--llvm-tools DIR`
|
|
373
|
+
can point to those tools when they are unpacked separately. Reports are written
|
|
374
|
+
to `target/coverage/{rust,swift}`. CI runs Rust coverage on Linux, Windows and
|
|
375
|
+
both Mac architectures, and Swift coverage on both Mac architectures.
|
|
376
|
+
|
|
377
|
+
## Publishing
|
|
378
|
+
|
|
379
|
+
All distributions are released from this repository by
|
|
380
|
+
`.github/workflows/release.yml`. A push to `main` checks the common package
|
|
381
|
+
version. An unreleased version is built and published automatically; a version
|
|
382
|
+
whose managed GitHub Release is already published is skipped. To release a new
|
|
383
|
+
version, update `Cargo.toml` and `pyproject.toml` together,
|
|
384
|
+
run `cargo generate-lockfile` to update `Cargo.lock`, commit the changes and
|
|
385
|
+
push to `main`. Use an `X.Y.Z` version. Do not create or move release tags
|
|
386
|
+
manually.
|
|
387
|
+
|
|
388
|
+
The workflow performs these steps:
|
|
389
|
+
|
|
390
|
+
1. Test the Python API/CLI and release tooling on Linux, macOS and Windows;
|
|
391
|
+
test Rust and build/install wheels across all five platform targets.
|
|
392
|
+
2. Build native archives, the offline Swift package and the universal
|
|
393
|
+
XCFramework. Release compilation uses Rust **1.94.1** throughout, including
|
|
394
|
+
the Linux containers; update the pin and package version together when
|
|
395
|
+
changing the release toolchain.
|
|
396
|
+
3. Compute the XCFramework archive checksum and create a detached release
|
|
397
|
+
commit that changes only `Package.swift`. Its parent is the tested source
|
|
398
|
+
commit. `main` keeps its local development manifest.
|
|
399
|
+
4. Build the sdist and crate from that exact release commit, then retain the
|
|
400
|
+
complete, checksummed release bundle as a GitHub Actions artifact.
|
|
401
|
+
5. Push `v<version>` at the prepared commit and populate a draft GitHub Release
|
|
402
|
+
with the wheels, sources, native archives and checksum manifest.
|
|
403
|
+
6. Publish to PyPI and crates.io, then publish the populated GitHub Release.
|
|
404
|
+
The Swift dependency URL is this repository and the same version tag.
|
|
405
|
+
|
|
406
|
+
The tag and publications happen in the same workflow run: tags pushed using
|
|
407
|
+
`GITHUB_TOKEN` do not trigger another release run. Only the publication job
|
|
408
|
+
uses the `release` GitHub environment, OIDC `id-token: write` and
|
|
409
|
+
`contents: write`; no additional Swift repository or personal token is needed.
|
|
410
|
+
Repository tag rules must allow this workflow to create the version tag.
|
|
411
|
+
The Git repository and release assets must be accessible to Swift consumers.
|
|
412
|
+
|
|
413
|
+
One-time publisher setup:
|
|
414
|
+
|
|
415
|
+
* **PyPI:** configure the Trusted Publisher for owner `mutating`, repository
|
|
416
|
+
`benchset`, workflow filename `release.yml` and environment `release`.
|
|
417
|
+
An already configured publisher with these values continues to work.
|
|
418
|
+
* **crates.io:** sign in with GitHub, verify the account email, and create an
|
|
419
|
+
API token with publishing permission. Store it as the **repository secret**
|
|
420
|
+
`CARGO_REGISTRY_TOKEN`. Missing credentials stop publication before a tag is
|
|
421
|
+
created. Rust consumers do not need an account. See the
|
|
422
|
+
[Cargo publishing guide](https://doc.rust-lang.org/cargo/reference/publishing.html).
|
|
423
|
+
|
|
424
|
+
To validate packaging without publishing, run the **Release** workflow
|
|
425
|
+
manually with `publish` left unchecked. To retry an incomplete release, run it
|
|
426
|
+
from `main` with `publish` checked, or rerun the failed publication job. If its
|
|
427
|
+
managed tag already exists, the workflow retrieves the original frozen bundle
|
|
428
|
+
using the workflow run ID recorded in the release commit; it does not rebuild
|
|
429
|
+
or move the tag. Uploaded PyPI/crates.io files are skipped only after their
|
|
430
|
+
checksums match. GitHub asset replacement is limited to the draft release.
|
|
431
|
+
Once all channels succeed, another push of the same version performs no uploads.
|
|
432
|
+
|
|
433
|
+
Frozen Actions bundles are retained for 90 days (subject to repository retention
|
|
434
|
+
settings). Resume an incomplete publication while its original bundle is
|
|
435
|
+
available. Code changes after a frozen release require a new version; a retry
|
|
436
|
+
continues to publish the original source snapshot. Concurrent release runs are
|
|
437
|
+
serialized. Local builds and build-only workflow runs do not publish anything.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// swift-tools-version:5.3
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
// Development checkouts and the offline ZIP use a locally built XCFramework.
|
|
5
|
+
// Release tags replace this binary target with an immutable URL and checksum.
|
|
6
|
+
let package = Package(
|
|
7
|
+
name: "Benchset",
|
|
8
|
+
platforms: [.macOS(.v10_12)],
|
|
9
|
+
products: [.library(name: "Benchset", targets: ["Benchset"])],
|
|
10
|
+
targets: [
|
|
11
|
+
.binaryTarget(name: "CBenchset",
|
|
12
|
+
url: "https://github.com/mutating/benchset/releases/download/v0.0.2/Benchset.xcframework.zip",
|
|
13
|
+
checksum: "29af959b4b1604abb0365f197cfceb73b7c54dc1713e24541b82b04c1956268f"),
|
|
14
|
+
.target(name: "Benchset", dependencies: ["CBenchset"]),
|
|
15
|
+
.testTarget(name: "BenchsetTests", dependencies: ["Benchset"], path: "tests/swift")
|
|
16
|
+
]
|
|
17
|
+
)
|