flopscope 0.2.0__py3-none-any.whl
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.
- benchmarks/__init__.py +1 -0
- benchmarks/__main__.py +6 -0
- benchmarks/_baseline.py +171 -0
- benchmarks/_bitwise.py +231 -0
- benchmarks/_complex.py +176 -0
- benchmarks/_contractions.py +291 -0
- benchmarks/_fft.py +198 -0
- benchmarks/_impl_urls.py +139 -0
- benchmarks/_linalg.py +197 -0
- benchmarks/_linalg_delegates.py +407 -0
- benchmarks/_metadata.py +141 -0
- benchmarks/_misc.py +653 -0
- benchmarks/_perf.py +321 -0
- benchmarks/_perm_group_calibration.py +175 -0
- benchmarks/_pointwise.py +372 -0
- benchmarks/_polynomial.py +193 -0
- benchmarks/_random.py +209 -0
- benchmarks/_reductions.py +136 -0
- benchmarks/_sorting.py +289 -0
- benchmarks/_stats.py +137 -0
- benchmarks/_window.py +92 -0
- benchmarks/accumulation/__init__.py +0 -0
- benchmarks/accumulation/bench_cost_compute.py +138 -0
- benchmarks/dashboard.py +312 -0
- benchmarks/runner.py +636 -0
- flopscope/__init__.py +273 -0
- flopscope/_accumulation/__init__.py +13 -0
- flopscope/_accumulation/_bipartite.py +121 -0
- flopscope/_accumulation/_burnside.py +51 -0
- flopscope/_accumulation/_cache.py +146 -0
- flopscope/_accumulation/_components.py +153 -0
- flopscope/_accumulation/_cost.py +1414 -0
- flopscope/_accumulation/_cost_descriptions.py +63 -0
- flopscope/_accumulation/_detection.py +318 -0
- flopscope/_accumulation/_ladder.py +191 -0
- flopscope/_accumulation/_output_orbit.py +104 -0
- flopscope/_accumulation/_partition.py +290 -0
- flopscope/_accumulation/_path_info.py +211 -0
- flopscope/_accumulation/_public.py +169 -0
- flopscope/_accumulation/_reduction.py +310 -0
- flopscope/_accumulation/_regimes.py +303 -0
- flopscope/_accumulation/_shape.py +33 -0
- flopscope/_accumulation/_wreath.py +209 -0
- flopscope/_budget.py +1027 -0
- flopscope/_config.py +118 -0
- flopscope/_counting_ops.py +451 -0
- flopscope/_display.py +478 -0
- flopscope/_docstrings.py +59 -0
- flopscope/_dtypes.py +20 -0
- flopscope/_einsum.py +717 -0
- flopscope/_errstate.py +25 -0
- flopscope/_flops.py +282 -0
- flopscope/_free_ops.py +2654 -0
- flopscope/_ndarray.py +1126 -0
- flopscope/_opt_einsum/LICENSE +21 -0
- flopscope/_opt_einsum/NOTICE +59 -0
- flopscope/_opt_einsum/__init__.py +209 -0
- flopscope/_opt_einsum/_contract.py +1478 -0
- flopscope/_opt_einsum/_helpers.py +164 -0
- flopscope/_opt_einsum/_hsluv.py +273 -0
- flopscope/_opt_einsum/_path_random.py +462 -0
- flopscope/_opt_einsum/_paths.py +1653 -0
- flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
- flopscope/_opt_einsum/_symmetry.py +140 -0
- flopscope/_opt_einsum/_typing.py +37 -0
- flopscope/_perm_group.py +717 -0
- flopscope/_pointwise.py +2522 -0
- flopscope/_polynomial.py +278 -0
- flopscope/_registry.py +3216 -0
- flopscope/_sorting_ops.py +571 -0
- flopscope/_symmetric.py +812 -0
- flopscope/_symmetry_transport.py +510 -0
- flopscope/_symmetry_utils.py +669 -0
- flopscope/_type_info.py +12 -0
- flopscope/_unwrap.py +70 -0
- flopscope/_validation.py +83 -0
- flopscope/_version_check.py +46 -0
- flopscope/_weights.py +195 -0
- flopscope/_window.py +177 -0
- flopscope/accounting.py +565 -0
- flopscope/data/default_weights.json +462 -0
- flopscope/data/weights.csv +509 -0
- flopscope/errors.py +197 -0
- flopscope/numpy/__init__.py +878 -0
- flopscope/numpy/fft/__init__.py +55 -0
- flopscope/numpy/fft/_free.py +51 -0
- flopscope/numpy/fft/_transforms.py +695 -0
- flopscope/numpy/linalg/__init__.py +105 -0
- flopscope/numpy/linalg/_aliases.py +126 -0
- flopscope/numpy/linalg/_compound.py +161 -0
- flopscope/numpy/linalg/_decompositions.py +353 -0
- flopscope/numpy/linalg/_properties.py +533 -0
- flopscope/numpy/linalg/_solvers.py +444 -0
- flopscope/numpy/linalg/_svd.py +122 -0
- flopscope/numpy/random/__init__.py +684 -0
- flopscope/numpy/random/_cost_formulas.py +115 -0
- flopscope/numpy/random/_counted_classes.py +241 -0
- flopscope/numpy/testing/__init__.py +13 -0
- flopscope/numpy/typing/__init__.py +30 -0
- flopscope/py.typed +0 -0
- flopscope/stats/__init__.py +84 -0
- flopscope/stats/_base.py +77 -0
- flopscope/stats/_cauchy.py +146 -0
- flopscope/stats/_erf.py +190 -0
- flopscope/stats/_expon.py +146 -0
- flopscope/stats/_laplace.py +150 -0
- flopscope/stats/_logistic.py +148 -0
- flopscope/stats/_lognorm.py +160 -0
- flopscope/stats/_ndtri.py +133 -0
- flopscope/stats/_norm.py +149 -0
- flopscope/stats/_truncnorm.py +186 -0
- flopscope/stats/_uniform.py +141 -0
- flopscope-0.2.0.dist-info/METADATA +23 -0
- flopscope-0.2.0.dist-info/RECORD +115 -0
- flopscope-0.2.0.dist-info/WHEEL +4 -0
benchmarks/_metadata.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Hardware & software metadata collection for benchmark reproducibility."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import datetime
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _read_cpu_model() -> str:
|
|
16
|
+
"""Read CPU model from /proc/cpuinfo (Linux) or fall back to platform."""
|
|
17
|
+
cpuinfo = Path("/proc/cpuinfo")
|
|
18
|
+
if cpuinfo.exists():
|
|
19
|
+
for line in cpuinfo.read_text().splitlines():
|
|
20
|
+
if line.startswith("model name"):
|
|
21
|
+
return line.split(":", 1)[1].strip()
|
|
22
|
+
return platform.processor() or "unknown"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _read_cache_info() -> dict[str, int | None]:
|
|
26
|
+
"""Read CPU cache sizes from sysfs (Linux only)."""
|
|
27
|
+
cache_dir = Path("/sys/devices/system/cpu/cpu0/cache")
|
|
28
|
+
info: dict[str, int | None] = {"L1d": None, "L1i": None, "L2": None, "L3": None}
|
|
29
|
+
if not cache_dir.exists():
|
|
30
|
+
return info
|
|
31
|
+
for idx in cache_dir.iterdir():
|
|
32
|
+
if not idx.is_dir():
|
|
33
|
+
continue
|
|
34
|
+
try:
|
|
35
|
+
level = (idx / "level").read_text().strip()
|
|
36
|
+
ctype = (idx / "type").read_text().strip()
|
|
37
|
+
size_str = (idx / "size").read_text().strip()
|
|
38
|
+
# size_str is like "32K", "256K", "8192K"
|
|
39
|
+
size_kb = int(size_str.rstrip("K"))
|
|
40
|
+
if level == "1" and ctype == "Data":
|
|
41
|
+
info["L1d"] = size_kb
|
|
42
|
+
elif level == "1" and ctype == "Instruction":
|
|
43
|
+
info["L1i"] = size_kb
|
|
44
|
+
elif level == "2":
|
|
45
|
+
info["L2"] = size_kb
|
|
46
|
+
elif level == "3":
|
|
47
|
+
info["L3"] = size_kb
|
|
48
|
+
except (OSError, ValueError):
|
|
49
|
+
continue
|
|
50
|
+
return info
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _cpu_cores_threads() -> tuple[int, int]:
|
|
54
|
+
"""Return (physical cores, logical threads)."""
|
|
55
|
+
try:
|
|
56
|
+
import psutil
|
|
57
|
+
|
|
58
|
+
return psutil.cpu_count(logical=False) or 1, psutil.cpu_count(logical=True) or 1
|
|
59
|
+
except ImportError:
|
|
60
|
+
count = os.cpu_count() or 1
|
|
61
|
+
return count, count
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _ram_gb() -> float:
|
|
65
|
+
"""Return total RAM in GB."""
|
|
66
|
+
try:
|
|
67
|
+
import psutil
|
|
68
|
+
|
|
69
|
+
return round(psutil.virtual_memory().total / (1024**3), 1)
|
|
70
|
+
except ImportError:
|
|
71
|
+
return 0.0
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _blas_info() -> str:
|
|
75
|
+
"""Extract BLAS library info from NumPy build config."""
|
|
76
|
+
try:
|
|
77
|
+
info = np.show_config(mode="dicts")
|
|
78
|
+
# NumPy >= 2.0 returns a dict with "Build Dependencies" key
|
|
79
|
+
if isinstance(info, dict):
|
|
80
|
+
blas = info.get("Build Dependencies", {}).get("blas", {})
|
|
81
|
+
if isinstance(blas, dict):
|
|
82
|
+
name = blas.get("name", "unknown")
|
|
83
|
+
version = blas.get("version", "unknown")
|
|
84
|
+
return f"{name} {version}"
|
|
85
|
+
return "unknown"
|
|
86
|
+
except Exception:
|
|
87
|
+
return "unknown"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
PERF_EVENTS = [
|
|
91
|
+
"fp_arith_inst_retired.scalar_single",
|
|
92
|
+
"fp_arith_inst_retired.scalar_double",
|
|
93
|
+
"fp_arith_inst_retired.128b_packed_single",
|
|
94
|
+
"fp_arith_inst_retired.128b_packed_double",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def collect_metadata(dtype: str, repeats: int, distributions: int) -> dict[str, Any]:
|
|
99
|
+
"""Collect hardware/software metadata for a benchmark run.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
dtype : str
|
|
104
|
+
NumPy dtype string (e.g. "float64").
|
|
105
|
+
repeats : int
|
|
106
|
+
Number of timing repetitions per operation.
|
|
107
|
+
distributions : int
|
|
108
|
+
Number of input distributions to sample.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
dict
|
|
113
|
+
Metadata dictionary with timestamp, hardware, software, and
|
|
114
|
+
benchmark_config sections.
|
|
115
|
+
"""
|
|
116
|
+
cores, threads = _cpu_cores_threads()
|
|
117
|
+
cache = _read_cache_info()
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
"timestamp": datetime.datetime.now(datetime.timezone.utc).isoformat(),
|
|
121
|
+
"hardware": {
|
|
122
|
+
"cpu_model": _read_cpu_model(),
|
|
123
|
+
"cpu_cores": cores,
|
|
124
|
+
"cpu_threads": threads,
|
|
125
|
+
"ram_gb": _ram_gb(),
|
|
126
|
+
"arch": platform.machine(),
|
|
127
|
+
"cache": cache,
|
|
128
|
+
},
|
|
129
|
+
"software": {
|
|
130
|
+
"os": f"{platform.system()} {platform.release()}",
|
|
131
|
+
"python": sys.version,
|
|
132
|
+
"numpy": np.__version__,
|
|
133
|
+
"blas": _blas_info(),
|
|
134
|
+
},
|
|
135
|
+
"benchmark_config": {
|
|
136
|
+
"dtype": dtype,
|
|
137
|
+
"repeats": repeats,
|
|
138
|
+
"distributions": distributions,
|
|
139
|
+
"perf_events": PERF_EVENTS,
|
|
140
|
+
},
|
|
141
|
+
}
|