torch-prisma 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZaberKo
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,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: torch-prisma
3
+ Version: 0.1.0
4
+ Summary: Measure PyTorch inference latency, MACs, FLOPs, and parameters
5
+ Keywords: pytorch,inference,benchmarking,latency,model-complexity,macs,flops,cuda,ascend
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 6 - Mature
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: System :: Benchmark
14
+ Requires-Dist: torch>=2.6
15
+ Requires-Python: >=3.10
16
+ Project-URL: Documentation, https://github.com/ZaberKo/torch-prisma/blob/main/docs/index.md
17
+ Project-URL: Issues, https://github.com/ZaberKo/torch-prisma/issues
18
+ Project-URL: Repository, https://github.com/ZaberKo/torch-prisma
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Torch Prisma
22
+
23
+ Measure PyTorch inference latency, parameters, MACs, and FLOPs on CPU, CUDA, and Huawei Ascend.
24
+
25
+ ## Installation
26
+
27
+ Requires Python 3.10+ and PyTorch 2.6+. Install in a virtual environment.
28
+
29
+ ```bash
30
+ uv pip install torch-prisma
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ Latency uses `torch.compile` by default. Set `compile=False` for eager timing.
36
+
37
+ ```python
38
+ import torch
39
+ from torch import nn
40
+
41
+ from torch_prisma import calculate_model_complexity, measure_module_latency
42
+
43
+ # For inference, set the model to evaluation mode and place it and the inputs on the device.
44
+ device = "cpu"
45
+ model = nn.Linear(8, 4).eval().to(device)
46
+ x = torch.randn(2, 8, device=device)
47
+
48
+ complexity = calculate_model_complexity(model, x)
49
+ latency = measure_module_latency(model, x, device=device)
50
+
51
+ print(complexity.params, complexity.macs, complexity.flops)
52
+ # Operations with incomplete counts, grouped by MACs and FLOPs.
53
+ print(complexity.uncovered_ops)
54
+ print(latency.median_ms)
55
+ ```
56
+
57
+ [Measurement API](https://github.com/ZaberKo/torch-prisma/blob/main/docs/measurement.md) · [Latency benchmarks](https://github.com/ZaberKo/torch-prisma/blob/main/docs/benchmarks.md) · [Huawei Ascend](https://github.com/ZaberKo/torch-prisma/blob/main/docs/ascend.md)
58
+
59
+ ## Development
60
+
61
+ ```bash
62
+ uv sync
63
+ uv run pre-commit install
64
+ uv run pytest
65
+ uv build
66
+ ```
67
+
68
+ Preview the documentation with `uv run --group docs zensical serve`.
69
+
70
+ ## License
71
+
72
+ Torch Prisma is distributed under the [MIT License](https://github.com/ZaberKo/torch-prisma/blob/main/LICENSE).
@@ -0,0 +1,52 @@
1
+ # Torch Prisma
2
+
3
+ Measure PyTorch inference latency, parameters, MACs, and FLOPs on CPU, CUDA, and Huawei Ascend.
4
+
5
+ ## Installation
6
+
7
+ Requires Python 3.10+ and PyTorch 2.6+. Install in a virtual environment.
8
+
9
+ ```bash
10
+ uv pip install torch-prisma
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Latency uses `torch.compile` by default. Set `compile=False` for eager timing.
16
+
17
+ ```python
18
+ import torch
19
+ from torch import nn
20
+
21
+ from torch_prisma import calculate_model_complexity, measure_module_latency
22
+
23
+ # For inference, set the model to evaluation mode and place it and the inputs on the device.
24
+ device = "cpu"
25
+ model = nn.Linear(8, 4).eval().to(device)
26
+ x = torch.randn(2, 8, device=device)
27
+
28
+ complexity = calculate_model_complexity(model, x)
29
+ latency = measure_module_latency(model, x, device=device)
30
+
31
+ print(complexity.params, complexity.macs, complexity.flops)
32
+ # Operations with incomplete counts, grouped by MACs and FLOPs.
33
+ print(complexity.uncovered_ops)
34
+ print(latency.median_ms)
35
+ ```
36
+
37
+ [Measurement API](https://github.com/ZaberKo/torch-prisma/blob/main/docs/measurement.md) · [Latency benchmarks](https://github.com/ZaberKo/torch-prisma/blob/main/docs/benchmarks.md) · [Huawei Ascend](https://github.com/ZaberKo/torch-prisma/blob/main/docs/ascend.md)
38
+
39
+ ## Development
40
+
41
+ ```bash
42
+ uv sync
43
+ uv run pre-commit install
44
+ uv run pytest
45
+ uv build
46
+ ```
47
+
48
+ Preview the documentation with `uv run --group docs zensical serve`.
49
+
50
+ ## License
51
+
52
+ Torch Prisma is distributed under the [MIT License](https://github.com/ZaberKo/torch-prisma/blob/main/LICENSE).
@@ -0,0 +1,53 @@
1
+ # Huawei Ascend
2
+
3
+ ## Runtime setup
4
+
5
+ Install the Ascend driver, CANN, and compatible PyTorch and TorchNPU versions. Initialize CANN before starting Python. The default compiled timing backend, TorchAir, is bundled in supported TorchNPU distributions. [Ascend compatibility matrix](https://github.com/Ascend/pytorch/blob/master/COMPATIBILITY.en.md) · [TorchAir](https://github.com/Ascend/torchair)
6
+
7
+ ## Measuring latency
8
+
9
+ ```python
10
+ import torch
11
+ import torch_npu
12
+ from torch import nn
13
+
14
+ from torch_prisma import measure_module_latency
15
+
16
+ device = torch.device("npu:0")
17
+ model = nn.Linear(128, 64, device=device).eval()
18
+ inputs = torch.randn(8, 128, device=device)
19
+
20
+ print(measure_module_latency(model, inputs, device).median_ms)
21
+ ```
22
+
23
+ TorchAir is the default compiler backend on Ascend. If it raises a compatibility error, use `compile=False` for eager timing.
24
+
25
+ To configure TorchAir, create a `CompilerConfig` and pass the resulting backend to the measurement function. This example enables graph dumps.
26
+
27
+ ```python
28
+ import torchair
29
+ from torchair.configs.compiler_config import CompilerConfig
30
+
31
+ config = CompilerConfig()
32
+ config.debug.graph_dump.type = "pbtxt"
33
+ backend = torchair.get_npu_backend(compiler_config=config)
34
+ latency = measure_module_latency(model, inputs, device, compile_kwargs={"backend": backend})
35
+ ```
36
+
37
+ See the [TorchAir documentation](https://github.com/Ascend/torchair) for configuration options.
38
+
39
+ ## Triton Ascend
40
+
41
+ To use [Triton Ascend](https://github.com/triton-lang/triton-ascend) through PyTorch Inductor, install a compatible Triton Ascend, CANN, PyTorch, and TorchNPU combination. With `compile=True` (the default), select the Inductor backend explicitly:
42
+
43
+ ```python
44
+ latency = measure_module_latency(model, inputs, device, compile_kwargs={"backend": "inductor"})
45
+ ```
46
+
47
+ TorchNPU uses Triton as its default Inductor compilation mode. If `TORCHINDUCTOR_NPU_BACKEND` has been changed, set it to `default` before the first compilation to select Triton. See the [TorchNPU backend documentation](https://github.com/Ascend/pytorch/blob/master/docs/zh/api/environment_variable/inductor/TORCHINDUCTOR_NPU_BACKEND.md).
48
+
49
+ For a callable that launches a Triton kernel directly, use eager timing:
50
+
51
+ ```python
52
+ latency = measure_module_latency(triton_callable, (x, y, output), device, compile=False)
53
+ ```
@@ -0,0 +1,73 @@
1
+ # Latency benchmarks
2
+
3
+ These measurements examine variation across independent process starts. Each process calls `measure_module_latency()` once. Each supported combination has 16 results: eight on each of two physical cards. The two cards were tested concurrently, with case order shuffled. Results retain all timed samples, including outliers.
4
+
5
+ Each test uses one accelerator, one PyTorch intra-op thread, randomly initialized TorchVision weights, and a fixed FP32 input of shape `(1, 3, 224, 224)`. The model is in evaluation mode, and weights and input use a fixed seed. The warmup and sample counts are shown below. Compiled measurements also use three preparation forwards. Inductor's disk cache was reused between processes separately for each card.
6
+
7
+ The latency columns are the averages of the 16 returned `mean_ms` and `median_ms` values. CV means *coefficient of variation*: the sample standard deviation divided by the average, expressed as a percentage. Each CV pair reports the variation of the returned mean and median, respectively. Combined CV uses all 16 processes; card CV uses the eight processes on that card. The Ascend test used cards 6 and 7; the 3080 Ti test used cards 0 and 1.
8
+
9
+ ## Ascend 910B2
10
+
11
+ The Ascend environment used PyTorch 2.10.0, TorchVision 0.25.0, TorchNPU 2.10.0.post7, Triton Ascend 3.6.0, CANN 9.2.0-beta.2, and driver 26.1.1. Inductor used Triton mode and four compiler threads. TorchAir is the default Ascend compiler backend.
12
+
13
+ | Model | Execution | Warmup / samples | Mean (ms) | Median (ms) | Combined CV | Card 6 CV | Card 7 CV |
14
+ | --- | --- | --- | ---: | ---: | ---: | ---: | ---: |
15
+ | MobileNetV3-Small | Eager | 5 / 20 | 6.895 | 6.804 | 4.00% / 3.12% | 2.92% / 2.51% | 5.04% / 3.72% |
16
+ | MobileNetV3-Small | Eager | 20 / 100 | 6.612 | 6.559 | 4.65% / 4.40% | 4.64% / 4.75% | 4.96% / 4.20% |
17
+ | MobileNetV3-Small | Eager | 50 / 20 | 6.365 | 6.343 | 3.86% / 3.49% | 3.72% / 3.00% | 3.74% / 3.77% |
18
+ | MobileNetV3-Small | TorchAir | 5 / 20 | 1.724 | 1.704 | 3.66% / 3.77% | 3.89% / 4.05% | 3.55% / 3.62% |
19
+ | MobileNetV3-Small | TorchAir | 20 / 100 | 1.744 | 1.739 | 4.28% / 4.29% | 4.28% / 4.30% | 4.47% / 4.50% |
20
+ | MobileNetV3-Small | TorchAir | 50 / 20 | 1.748 | 1.723 | 4.46% / 4.37% | 4.87% / 4.51% | 4.19% / 4.42% |
21
+ | MobileNetV3-Small | Triton Inductor | 5 / 20 | 7.488 | 7.477 | 4.85% / 4.98% | 5.60% / 5.77% | 3.37% / 3.52% |
22
+ | MobileNetV3-Small | Triton Inductor | 20 / 100 | 7.400 | 7.374 | 3.23% / 3.12% | 2.28% / 2.15% | 3.81% / 3.76% |
23
+ | MobileNetV3-Small | Triton Inductor | 50 / 20 | 7.303 | 7.293 | 3.91% / 3.88% | 4.09% / 3.95% | 4.01% / 4.07% |
24
+ | ResNet-50 | Eager | 5 / 20 | 7.481 | 7.451 | 6.15% / 6.15% | 7.04% / 7.14% | 5.60% / 5.47% |
25
+ | ResNet-50 | Eager | 20 / 100 | 7.235 | 7.196 | 4.38% / 4.27% | 3.69% / 3.71% | 5.13% / 4.97% |
26
+ | ResNet-50 | Eager | 50 / 20 | 7.181 | 7.162 | 3.21% / 3.25% | 3.07% / 3.28% | 3.38% / 3.31% |
27
+ | ResNet-50 | TorchAir | 5 / 20 | 2.378 | 2.361 | 4.05% / 4.07% | 3.98% / 4.02% | 4.38% / 4.40% |
28
+ | ResNet-50 | TorchAir | 20 / 100 | 2.375 | 2.371 | 3.77% / 3.76% | 4.26% / 4.25% | 0.37% / 0.36% |
29
+ | ResNet-50 | TorchAir | 50 / 20 | 2.359 | 2.338 | 4.03% / 4.12% | 4.24% / 4.33% | 4.09% / 4.20% |
30
+ | ResNet-50 | Triton Inductor | 5 / 20 | 12.365 | 12.324 | 0.29% / 0.28% | 0.20% / 0.22% | 0.34% / 0.29% |
31
+ | ResNet-50 | Triton Inductor | 20 / 100 | 12.329 | 12.321 | 0.35% / 0.35% | 0.19% / 0.19% | 0.25% / 0.25% |
32
+ | ResNet-50 | Triton Inductor | 50 / 20 | 12.376 | 12.321 | 0.32% / 0.35% | 0.13% / 0.24% | 0.18% / 0.21% |
33
+ | ViT-B/16 | Eager | 5 / 20 | 8.810 | 8.799 | 4.79% / 4.49% | 3.05% / 2.64% | 6.19% / 5.90% |
34
+ | ViT-B/16 | Eager | 20 / 100 | 8.344 | 8.311 | 6.71% / 6.14% | 9.26% / 8.45% | 3.28% / 3.08% |
35
+ | ViT-B/16 | Eager | 50 / 20 | 8.278 | 8.253 | 4.42% / 4.39% | 5.82% / 5.72% | 2.72% / 2.69% |
36
+ | ViT-B/16 | Triton Inductor | 5 / 20 | 10.945 | 10.899 | 2.51% / 2.43% | 2.02% / 1.94% | 2.98% / 2.88% |
37
+ | ViT-B/16 | Triton Inductor | 20 / 100 | 10.578 | 10.553 | 3.05% / 3.15% | 2.44% / 2.44% | 3.48% / 3.66% |
38
+ | ViT-B/16 | Triton Inductor | 50 / 20 | 10.672 | 10.643 | 5.36% / 5.21% | 5.91% / 5.74% | 5.01% / 4.89% |
39
+
40
+ ViT-B/16 with TorchAir failed during compilation preparation on both cards with `only supported softmax_layout=TND`; no latency statistics are available for that combination.
41
+
42
+ ## GeForce RTX 3080 Ti
43
+
44
+ The 3080 Ti environment used PyTorch 2.15.0, TorchVision 0.29.2, CUDA runtime 13.2, Triton 3.9.0, and NVIDIA driver 595.71.05. Inductor used four compiler threads. Software versions differ between devices, so these results are not a controlled cross-device performance comparison.
45
+
46
+ | Model | Execution | Warmup / samples | Mean (ms) | Median (ms) | Combined CV | Card 0 CV | Card 1 CV |
47
+ | --- | --- | --- | ---: | ---: | ---: | ---: | ---: |
48
+ | MobileNetV3-Small | Eager | 5 / 20 | 4.302 | 4.267 | 11.07% / 10.99% | 13.12% / 12.99% | 2.31% / 1.34% |
49
+ | MobileNetV3-Small | Eager | 20 / 100 | 4.399 | 4.383 | 12.16% / 12.36% | 11.37% / 11.69% | 13.54% / 13.61% |
50
+ | MobileNetV3-Small | Eager | 50 / 20 | 4.149 | 4.127 | 6.94% / 7.10% | 2.00% / 1.86% | 9.47% / 9.66% |
51
+ | MobileNetV3-Small | Inductor | 5 / 20 | 2.214 | 2.209 | 14.75% / 14.60% | 15.17% / 14.82% | 15.14% / 15.15% |
52
+ | MobileNetV3-Small | Inductor | 20 / 100 | 2.115 | 2.114 | 11.96% / 13.02% | 14.83% / 15.83% | 0.67% / 0.81% |
53
+ | MobileNetV3-Small | Inductor | 50 / 20 | 2.046 | 2.038 | 8.14% / 8.22% | 1.54% / 1.30% | 11.18% / 11.33% |
54
+ | ResNet-50 | Eager | 5 / 20 | 5.197 | 5.106 | 10.02% / 10.16% | 8.87% / 9.16% | 11.21% / 11.41% |
55
+ | ResNet-50 | Eager | 20 / 100 | 4.884 | 4.840 | 1.46% / 1.42% | 1.36% / 1.36% | 1.57% / 1.38% |
56
+ | ResNet-50 | Eager | 50 / 20 | 5.006 | 4.981 | 8.10% / 8.31% | 8.33% / 8.61% | 8.44% / 8.59% |
57
+ | ResNet-50 | Inductor | 5 / 20 | 2.656 | 2.612 | 15.85% / 13.01% | 10.20% / 10.24% | 18.49% / 14.66% |
58
+ | ResNet-50 | Inductor | 20 / 100 | 2.674 | 2.658 | 13.58% / 13.51% | 10.56% / 10.48% | 14.70% / 14.58% |
59
+ | ResNet-50 | Inductor | 50 / 20 | 2.613 | 2.590 | 15.67% / 14.44% | 10.95% / 11.09% | 19.15% / 17.22% |
60
+ | ViT-B/16 | Eager | 5 / 20 | 4.522 | 4.505 | 7.30% / 7.23% | 6.22% / 5.73% | 8.07% / 8.25% |
61
+ | ViT-B/16 | Eager | 20 / 100 | 4.224 | 4.151 | 7.89% / 8.41% | 2.35% / 1.45% | 10.07% / 10.50% |
62
+ | ViT-B/16 | Eager | 50 / 20 | 4.284 | 4.249 | 8.91% / 9.10% | 10.22% / 10.27% | 7.66% / 8.15% |
63
+ | ViT-B/16 | Inductor | 5 / 20 | 4.655 | 4.670 | 1.43% / 0.17% | 1.79% / 0.23% | 0.09% / 0.07% |
64
+ | ViT-B/16 | Inductor | 20 / 100 | 4.315 | 4.240 | 1.62% / 0.98% | 1.60% / 0.18% | 1.04% / 0.34% |
65
+ | ViT-B/16 | Inductor | 50 / 20 | 4.282 | 4.240 | 1.11% / 0.82% | 1.51% / 0.46% | 0.38% / 0.29% |
66
+
67
+ ## Interpreting the defaults
68
+
69
+ The default is five warmup forwards and 20 timed forwards. On Ascend, the combined mean and median CVs at 5/20 ranged from 0.28% to 6.15% across the measured combinations. Increasing warmup or sample counts did not consistently reduce variation across all models and backends.
70
+
71
+ On the 3080 Ti, the combined median CV at 5/20 ranged from 0.17% to 14.60%. For ViT-B/16 with Inductor, the returned median averaged 4.670 ms at 5/20 and 4.240 ms at 50/20, even though the 5/20 median CV was only 0.17%. A low CV therefore does not by itself establish that the measurement has reached a representative latency. Increasing warmup to 50 did not reduce CV for every measured combination.
72
+
73
+ These results do not establish either 5/20 or 50/20 as a reliable setting for every workload or device. Compare settings for the workload and environment being measured, and inspect the samples when the mean and median differ substantially.
@@ -0,0 +1,41 @@
1
+ # Torch Prisma
2
+
3
+ Torch Prisma measures PyTorch inference latency, parameters, multiply-accumulates (MACs), and floating-point operations (FLOPs) on CPU, CUDA, and Huawei Ascend.
4
+
5
+ ## Installation
6
+
7
+ Requires Python 3.10+ and PyTorch 2.6+. Install in a virtual environment.
8
+
9
+ ```bash
10
+ uv pip install torch-prisma
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Latency uses `torch.compile` by default. Set `compile=False` for eager timing.
16
+
17
+ ```python
18
+ import torch
19
+ from torch import nn
20
+
21
+ from torch_prisma import calculate_model_complexity, measure_module_latency
22
+
23
+ # For inference, set the model to evaluation mode and place it and the inputs on the device.
24
+ device = "cpu"
25
+ model = nn.Linear(8, 4).eval().to(device)
26
+ inputs = torch.randn(2, 8, device=device)
27
+
28
+ complexity = calculate_model_complexity(model, inputs)
29
+ latency = measure_module_latency(model, inputs, device=device)
30
+
31
+ print(complexity.params, complexity.macs, complexity.flops)
32
+ # Operations with incomplete counts, grouped by MACs and FLOPs.
33
+ print(complexity.uncovered_ops)
34
+ print(latency.median_ms)
35
+ ```
36
+
37
+ ## Guides
38
+
39
+ - [Measurement API and conventions](measurement.md)
40
+ - [Latency benchmarks](benchmarks.md)
41
+ - [Huawei Ascend](ascend.md)
@@ -0,0 +1,87 @@
1
+ # Measurement API
2
+
3
+ Import the measurement functions from `torch_prisma`. Set the model's training or evaluation mode and place the model and inputs on their execution devices before measuring. Forward side effects persist.
4
+
5
+ ## `measure_module_latency`
6
+
7
+ Measures one forward per sample for the entire batch in inference mode. CPU uses a wall clock. CUDA and Ascend use events on the selected device's current stream.
8
+
9
+ **Parameters**
10
+
11
+ - `target` — Prepared module or callable.
12
+ - `input_args` — Positional input or tuple/list of positional inputs. Use `(items,)` to pass a list as one argument.
13
+ - `device` — Timing device.
14
+ - `input_kwargs` — Optional keyword arguments.
15
+ - `repetitions` — Number of timed forwards. Defaults to 20.
16
+ - `warmup` — Number of untimed forwards after compilation preparation. Defaults to 5.
17
+ - `compile` — Enable `torch.compile`. Defaults to `True`.
18
+ - `compile_kwargs` — Keyword arguments passed to `torch.compile` when enabled. On Ascend, pass `{"backend": "inductor"}` to use Triton through Inductor. See the [Ascend guide](ascend.md#triton-ascend).
19
+
20
+ **Returns**
21
+
22
+ A `LatencyStats` result in milliseconds. Its `samples_ms` preserve sampling order. The remaining fields are `mean_ms`, `median_ms`, `std_ms` (population standard deviation), `min_ms`, `max_ms`, `p90_ms`, and `p99_ms`. Percentiles select the sample at index `min(int(n * q), n - 1)` after sorting, with `q` equal to 0.90 or 0.99. Use `LatencyStats.from_samples()` to summarize external timings and `to_dict()` to serialize the result.
23
+
24
+ **Raises**
25
+
26
+ - `ValueError` for invalid timing options or an unsupported timing device.
27
+ - `RuntimeError` when the accelerator or default Ascend backend is unavailable, or compilation is required after preparation.
28
+
29
+ Compilation runs three untimed forwards before warmup. Set `compile=False` for eager timing. See [Ascend setup](ascend.md) and [latency benchmarks](benchmarks.md).
30
+
31
+ ## `calculate_model_complexity`
32
+
33
+ Counts operations for one eager forward without gradients. Counting requests math scaled dot-product attention and disables the multihead attention fast path.
34
+
35
+ **Parameters**
36
+
37
+ - `target` — Prepared module or callable.
38
+ - `input_args` — Positional input or tuple/list of positional inputs.
39
+ - `input_kwargs` — Optional keyword arguments.
40
+
41
+ **Returns**
42
+
43
+ A `ModelComplexity` result for the supplied batch. `params` counts unique registered parameter elements. `macs` and `flops` are known operation subtotals. `uncovered_ops` lists visible operations with incomplete counts, grouped by MACs and FLOPs. A plain callable reports zero parameters. Use `to_dict()` to serialize the result.
44
+
45
+ Each real floating-point dense MAC contributes two FLOPs. Supported arithmetic outside matrix products adds to FLOPs. Comparisons, ReLU, forward max pooling, indexing, and data movement contribute zero FLOPs. Integer matrix work contributes MACs only. Complex matrix work contributes MACs and a FLOP coverage gap.
46
+
47
+ ## `count_parameters`
48
+
49
+ Counts the elements in unique registered parameter objects without executing a forward.
50
+
51
+ **Parameters**
52
+
53
+ - `model` — Module whose parameters are counted.
54
+
55
+ **Returns**
56
+
57
+ The parameter count as an integer. Distinct parameters sharing storage count separately.
58
+
59
+ ## `count_operations`
60
+
61
+ Counts operations for a prepared zero-argument eager callable.
62
+
63
+ **Parameters**
64
+
65
+ - `forward` — Callable that executes the forward to count.
66
+
67
+ **Returns**
68
+
69
+ An `OperationCounts` result containing `macs`, `flops`, and `uncovered_ops`.
70
+
71
+ ```python
72
+ from torch_prisma import count_operations
73
+
74
+ operations = count_operations(lambda: model(inputs))
75
+ ```
76
+
77
+ ## Nominal FLOP formulas
78
+
79
+ Each transcendental operation counts as one FLOP.
80
+
81
+ - Addition, subtraction, multiplication, and division contribute one FLOP per output element. A nonunit add/subtract scale contributes an additional multiplication.
82
+ - Sum over a nonempty group of `K` elements contributes `K - 1` additions. Mean adds one division.
83
+ - Softmax over a nonempty group of `K` elements contributes `4K - 1` FLOPs.
84
+ - LayerNorm and GroupNorm contribute `5K + 2` FLOPs per group of `K` elements, plus `K` for each affine weight or bias.
85
+ - BatchNorm with stored statistics contributes two FLOPs per channel and two per input element, plus one per element for each affine weight or bias.
86
+ - Matrix product bias and scaling contribute an addition per output element when the bias term is active, plus each applicable nonunit scalar multiplication.
87
+ - Convolution bias contributes one additional FLOP per output element.
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.9.11,<0.10.0"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "torch-prisma"
7
+ version = "0.1.0"
8
+ description = "Measure PyTorch inference latency, MACs, FLOPs, and parameters"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ dependencies = ["torch>=2.6"]
14
+ keywords = [
15
+ "pytorch",
16
+ "inference",
17
+ "benchmarking",
18
+ "latency",
19
+ "model-complexity",
20
+ "macs",
21
+ "flops",
22
+ "cuda",
23
+ "ascend",
24
+ ]
25
+ classifiers = [
26
+ "Development Status :: 6 - Mature",
27
+ "Intended Audience :: Developers",
28
+ "Intended Audience :: Science/Research",
29
+ "Programming Language :: Python :: 3 :: Only",
30
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
31
+ "Topic :: System :: Benchmark",
32
+ ]
33
+
34
+ [project.urls]
35
+ Repository = "https://github.com/ZaberKo/torch-prisma"
36
+ Documentation = "https://github.com/ZaberKo/torch-prisma/blob/main/docs/index.md"
37
+ Issues = "https://github.com/ZaberKo/torch-prisma/issues"
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "pre-commit; python_version >= '3.11'",
42
+ "pytest>=8",
43
+ "ruff",
44
+ ]
45
+ docs = ["zensical"]
46
+
47
+ [tool.uv.dependency-groups]
48
+ docs = { requires-python = ">=3.11" }
49
+
50
+ [tool.uv.build-backend]
51
+ module-root = ""
52
+ source-include = ["docs/**", "tests/**"]
53
+
54
+ [tool.pytest.ini_options]
55
+ minversion = "8.0"
56
+ testpaths = ["tests"]
57
+ markers = ["cuda: requires a CUDA runner", "ascend: requires an Ascend and TorchAir runner"]
58
+ addopts = ["-ra", "--strict-config", "--strict-markers", "--import-mode=importlib"]
59
+
60
+ [tool.ruff]
61
+ target-version = "py310"
62
+ line-length = 100
63
+
64
+ [tool.ruff.lint]
65
+ select = ["E", "F", "I", "UP", "B", "SIM", "C4", "RUF", "D"]
66
+ ignore = ["E501"]
67
+
68
+ [tool.ruff.lint.pydocstyle]
69
+ convention = "google"
70
+
71
+ [tool.ruff.lint.per-file-ignores]
72
+ "tests/**" = ["D"]
73
+
74
+ [tool.ruff.format]
75
+ docstring-code-format = true
@@ -0,0 +1,145 @@
1
+ import os
2
+ import sys
3
+ from types import SimpleNamespace
4
+ from unittest.mock import Mock
5
+
6
+ import pytest
7
+ import torch
8
+ from torch import nn
9
+
10
+ import torch_prisma.latency as latency
11
+ from torch_prisma import calculate_model_complexity, measure_module_latency
12
+
13
+
14
+ def test_ascend_compiler_and_event_wiring(monkeypatch):
15
+ device = SimpleNamespace(type="npu")
16
+ stream = object()
17
+ operations = []
18
+ forward_limits = []
19
+ config = torch._dynamo.config
20
+ limit_name = "recompile_limit" if hasattr(config, "recompile_limit") else "cache_size_limit"
21
+ accumulated_limit_name = f"accumulated_{limit_name}"
22
+ original_limits = getattr(config, limit_name), getattr(config, accumulated_limit_name)
23
+
24
+ class Event:
25
+ def __init__(self, *, enable_timing):
26
+ assert enable_timing
27
+
28
+ def record(self, selected_stream):
29
+ assert selected_stream is stream
30
+ operations.append("record")
31
+
32
+ def elapsed_time(self, end):
33
+ assert isinstance(end, Event)
34
+ return 0.25
35
+
36
+ def current_stream(selected):
37
+ assert selected is device
38
+ return stream
39
+
40
+ def synchronize(selected):
41
+ assert selected is device
42
+ operations.append("sync")
43
+
44
+ runtime = SimpleNamespace(Event=Event, current_stream=current_stream, synchronize=synchronize)
45
+
46
+ def timing_device(requested):
47
+ assert requested == "npu:0"
48
+ return device, runtime
49
+
50
+ def compile_fn(fn, *, backend, fullgraph):
51
+ assert backend == "torchair-test" and fullgraph
52
+ return fn
53
+
54
+ def forward(x):
55
+ operations.append("forward")
56
+ forward_limits.append(
57
+ (getattr(config, limit_name), getattr(config, accumulated_limit_name))
58
+ )
59
+ return x
60
+
61
+ backend_factory = Mock(return_value="torchair-test")
62
+ monkeypatch.setitem(sys.modules, "torchair", SimpleNamespace(get_npu_backend=backend_factory))
63
+ monkeypatch.setattr(latency, "_timing_device", timing_device)
64
+ monkeypatch.setattr(torch, "compile", compile_fn)
65
+ latency._default_npu_backend.cache_clear()
66
+ try:
67
+ assert latency._compile_options(device, {"backend": "custom"}) == {"backend": "custom"}
68
+ backend_factory.assert_not_called()
69
+ result = measure_module_latency(
70
+ forward,
71
+ torch.ones(1),
72
+ "npu:0",
73
+ repetitions=2,
74
+ warmup=1,
75
+ compile_kwargs={"fullgraph": True},
76
+ )
77
+ assert latency._compile_options(device, None) == {"backend": "torchair-test"}
78
+ backend_factory.assert_called_once_with()
79
+ finally:
80
+ latency._default_npu_backend.cache_clear()
81
+ assert result.samples_ms == (0.25, 0.25)
82
+ assert operations == (
83
+ ["forward", "forward", "forward", "sync", "forward"]
84
+ + ["record"] * 4
85
+ + ["sync", "record", "forward", "record", "record", "forward", "record", "sync"]
86
+ )
87
+ raised_limits = (2**31 - 1, 2**31 - 1)
88
+ assert forward_limits == [raised_limits] * 3 + [original_limits] * 3
89
+ assert (getattr(config, limit_name), getattr(config, accumulated_limit_name)) == original_limits
90
+
91
+
92
+ @pytest.mark.cuda
93
+ @pytest.mark.skipif(os.environ.get("TORCH_PRISMA_TEST_DEVICE") != "cuda", reason="Requires CUDA")
94
+ def test_cuda_measurements():
95
+ assert torch.cuda.is_available()
96
+ device = torch.device("cuda", torch.cuda.current_device())
97
+ model = nn.Linear(4, 3, device=device).eval()
98
+ x = torch.ones(2, 4, device=device)
99
+ stats = measure_module_latency(model, x, device, warmup=1, repetitions=3, compile=False)
100
+ assert len(stats.samples_ms) == 3
101
+ assert stats.median_ms > 0
102
+ complexity = calculate_model_complexity(model, x)
103
+ assert (complexity.params, complexity.macs, complexity.flops) == (15, 24, 54)
104
+
105
+
106
+ @pytest.mark.ascend
107
+ @pytest.mark.skipif(
108
+ os.environ.get("TORCH_PRISMA_TEST_DEVICE") != "ascend", reason="Requires Ascend"
109
+ )
110
+ def test_ascend_measurements():
111
+ __import__("torch_npu")
112
+ assert torch.npu.is_available()
113
+ device = torch.device("npu", torch.npu.current_device())
114
+ model = nn.Linear(4, 3, device=device).eval()
115
+ x = torch.ones(2, 4, device=device)
116
+ for compile in (False, True):
117
+ stats = measure_module_latency(model, x, device, warmup=2, repetitions=3, compile=compile)
118
+ assert len(stats.samples_ms) == 3
119
+ assert stats.median_ms > 0
120
+
121
+ complexity = calculate_model_complexity(model, x)
122
+ assert complexity.params == 15
123
+ assert complexity.macs == 24 or complexity.uncovered_ops["macs"]
124
+ assert complexity.flops == 54 or complexity.uncovered_ops["flops"]
125
+
126
+
127
+ @pytest.mark.ascend
128
+ @pytest.mark.skipif(
129
+ os.environ.get("TORCH_PRISMA_TEST_ASCEND_INDUCTOR") != "1",
130
+ reason="Requires Ascend with Triton Inductor",
131
+ )
132
+ def test_ascend_triton_inductor_measurement():
133
+ __import__("torch_npu")
134
+ device = torch.device("npu", torch.npu.current_device())
135
+ x = torch.randn(8192, device=device)
136
+
137
+ def forward(x):
138
+ return torch.sin(x) + x * x
139
+
140
+ stats = measure_module_latency(
141
+ forward, x, device, warmup=2, repetitions=3, compile_kwargs={"backend": "inductor"}
142
+ )
143
+ assert len(stats.samples_ms) == 3
144
+ assert stats.median_ms > 0
145
+ torch.testing.assert_close(torch.compile(forward, backend="inductor")(x), forward(x))