torchbenchx 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.
- torchbenchx-0.1.0/LICENSE +3 -0
- torchbenchx-0.1.0/MANIFEST.in +2 -0
- torchbenchx-0.1.0/PKG-INFO +129 -0
- torchbenchx-0.1.0/README.md +105 -0
- torchbenchx-0.1.0/pyproject.toml +35 -0
- torchbenchx-0.1.0/setup.cfg +4 -0
- torchbenchx-0.1.0/setup.py +33 -0
- torchbenchx-0.1.0/torchbenchx/__init__.py +6 -0
- torchbenchx-0.1.0/torchbenchx/benchx.py +376 -0
- torchbenchx-0.1.0/torchbenchx.egg-info/PKG-INFO +129 -0
- torchbenchx-0.1.0/torchbenchx.egg-info/SOURCES.txt +12 -0
- torchbenchx-0.1.0/torchbenchx.egg-info/dependency_links.txt +1 -0
- torchbenchx-0.1.0/torchbenchx.egg-info/requires.txt +6 -0
- torchbenchx-0.1.0/torchbenchx.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: torchbenchx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unified Deep Learning Benchmark Library for PyTorch and TensorFlow
|
|
5
|
+
Home-page: https://github.com/syeedsaquib/torchbenchx
|
|
6
|
+
Author: Syeed Mohd Saquib
|
|
7
|
+
Author-email: Syeed Mohd Saquib <syeed.saquib@example.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://pypi.org/project/torchbenchx/
|
|
10
|
+
Project-URL: Repository, https://github.com/syeedsaquib/torchbenchx
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: torch>=2.0
|
|
15
|
+
Requires-Dist: tensorflow>=2.12
|
|
16
|
+
Requires-Dist: plotly
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: psutil
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-python
|
|
24
|
+
|
|
25
|
+
# torchbenchx — Unified Deep Learning Benchmark Library
|
|
26
|
+
|
|
27
|
+
Benchmark. Compare. Visualize.
|
|
28
|
+
|
|
29
|
+
`torchbenchx` is a framework-agnostic deep learning benchmarking library that measures latency, throughput, memory usage, and parameter count of models from both PyTorch and TensorFlow — all inside your Jupyter Notebook, with no servers or dashboards required.
|
|
30
|
+
|
|
31
|
+
It’s designed for researchers, developers, and AI enthusiasts who want quick, reliable model comparisons with interactive visualizations.
|
|
32
|
+
|
|
33
|
+
## ⚙️ Key Features
|
|
34
|
+
|
|
35
|
+
- **Dual Framework Support**: Benchmark models from both PyTorch and TensorFlow using one unified API.
|
|
36
|
+
- **Latency & Throughput Measurement**: Calculates per-inference latency and samples-per-second throughput.
|
|
37
|
+
- **Model Parameters & Memory Usage**: Automatically estimates memory footprint and parameter count.
|
|
38
|
+
- **Device Awareness**: Detects and runs on CPU or GPU automatically.
|
|
39
|
+
- **Notebook-native Visualization**: Generates Plotly scatter plots of performance metrics directly in Jupyter/Colab.
|
|
40
|
+
- **Minimal Dependencies**: Lightweight and ready to use.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install torchbenchx
|
|
46
|
+
# Backends and viz libs
|
|
47
|
+
pip install torch torchvision tensorflow plotly pandas psutil
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Optional: install `notebook` or `jupyterlab` if using outside Colab.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from torchbenchx import BenchX
|
|
56
|
+
import torchvision.models as tv_models
|
|
57
|
+
import tensorflow as tf
|
|
58
|
+
|
|
59
|
+
bench = BenchX(batch_size=16, runs=30)
|
|
60
|
+
|
|
61
|
+
bench.benchmark(tv_models.resnet18(weights=None), "ResNet18", framework="pytorch")
|
|
62
|
+
bench.benchmark(tf.keras.applications.MobileNetV3Small(), "MobileNetV3", framework="tensorflow")
|
|
63
|
+
|
|
64
|
+
bench.summary() # Styled table
|
|
65
|
+
bench.visualize() # Plotly scatter
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Core Class: BenchX
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
BenchX(device=None, batch_size=16, runs=30, warmup=10)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **device**: "cuda" or "cpu" — auto-detected if not set (for PyTorch)
|
|
75
|
+
- **batch_size**: Number of samples per inference
|
|
76
|
+
- **runs**: Number of repeated runs for averaging
|
|
77
|
+
- **warmup**: Initial runs ignored to stabilize measurements
|
|
78
|
+
|
|
79
|
+
### Methods
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
benchmark(model, name, input_shape=(3, 224, 224), framework="pytorch")
|
|
83
|
+
```
|
|
84
|
+
Returns a dict with:
|
|
85
|
+
`{Model, Framework, Device, Latency_ms, Throughput_sps, Memory_MB, Params_M}`
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
summary() # Styled pandas DataFrame (stronger colors on white bg)
|
|
89
|
+
visualize(metric_x="Latency_ms", metric_y="Throughput_sps") # Scatter with improved legend/layout
|
|
90
|
+
visualize_all() # Returns dict of figures: scatter, throughput_bar, latency_bar, memory_bar
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Dependencies
|
|
94
|
+
|
|
95
|
+
- **torch**: PyTorch backend
|
|
96
|
+
- **tensorflow**: TensorFlow backend
|
|
97
|
+
- **plotly**: Interactive plotting
|
|
98
|
+
- **pandas**: Data handling and summary display
|
|
99
|
+
- **psutil**: Memory usage monitoring
|
|
100
|
+
|
|
101
|
+
## Internal Workflow
|
|
102
|
+
|
|
103
|
+
1. Warmup phase to stabilize performance
|
|
104
|
+
2. Timed inference loop across `runs`
|
|
105
|
+
3. Metrics: latency (ms/inference), throughput (samples/sec)
|
|
106
|
+
4. Memory estimation (GPU peak or process RSS)
|
|
107
|
+
5. Results aggregated into a DataFrame and visualized
|
|
108
|
+
6. Memory estimation: reports `Memory_Bytes`, `Memory_MB`, and a humanized `Memory`
|
|
109
|
+
|
|
110
|
+
## Use Cases
|
|
111
|
+
|
|
112
|
+
- **Research**: Compare architectures across frameworks
|
|
113
|
+
- **Development**: Optimize inference pipelines
|
|
114
|
+
- **Education**: Demonstrate benchmarking concepts
|
|
115
|
+
- **Model Selection**: Speed vs efficiency trade-offs
|
|
116
|
+
|
|
117
|
+
## Roadmap
|
|
118
|
+
|
|
119
|
+
- FLOPs estimation
|
|
120
|
+
- Power consumption metrics
|
|
121
|
+
- JAX and ONNX Runtime support
|
|
122
|
+
- Export results to CSV/JSON
|
|
123
|
+
- Leaderboard UI for reproducibility
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
128
|
+
|
|
129
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# torchbenchx — Unified Deep Learning Benchmark Library
|
|
2
|
+
|
|
3
|
+
Benchmark. Compare. Visualize.
|
|
4
|
+
|
|
5
|
+
`torchbenchx` is a framework-agnostic deep learning benchmarking library that measures latency, throughput, memory usage, and parameter count of models from both PyTorch and TensorFlow — all inside your Jupyter Notebook, with no servers or dashboards required.
|
|
6
|
+
|
|
7
|
+
It’s designed for researchers, developers, and AI enthusiasts who want quick, reliable model comparisons with interactive visualizations.
|
|
8
|
+
|
|
9
|
+
## ⚙️ Key Features
|
|
10
|
+
|
|
11
|
+
- **Dual Framework Support**: Benchmark models from both PyTorch and TensorFlow using one unified API.
|
|
12
|
+
- **Latency & Throughput Measurement**: Calculates per-inference latency and samples-per-second throughput.
|
|
13
|
+
- **Model Parameters & Memory Usage**: Automatically estimates memory footprint and parameter count.
|
|
14
|
+
- **Device Awareness**: Detects and runs on CPU or GPU automatically.
|
|
15
|
+
- **Notebook-native Visualization**: Generates Plotly scatter plots of performance metrics directly in Jupyter/Colab.
|
|
16
|
+
- **Minimal Dependencies**: Lightweight and ready to use.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install torchbenchx
|
|
22
|
+
# Backends and viz libs
|
|
23
|
+
pip install torch torchvision tensorflow plotly pandas psutil
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Optional: install `notebook` or `jupyterlab` if using outside Colab.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from torchbenchx import BenchX
|
|
32
|
+
import torchvision.models as tv_models
|
|
33
|
+
import tensorflow as tf
|
|
34
|
+
|
|
35
|
+
bench = BenchX(batch_size=16, runs=30)
|
|
36
|
+
|
|
37
|
+
bench.benchmark(tv_models.resnet18(weights=None), "ResNet18", framework="pytorch")
|
|
38
|
+
bench.benchmark(tf.keras.applications.MobileNetV3Small(), "MobileNetV3", framework="tensorflow")
|
|
39
|
+
|
|
40
|
+
bench.summary() # Styled table
|
|
41
|
+
bench.visualize() # Plotly scatter
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Core Class: BenchX
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
BenchX(device=None, batch_size=16, runs=30, warmup=10)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- **device**: "cuda" or "cpu" — auto-detected if not set (for PyTorch)
|
|
51
|
+
- **batch_size**: Number of samples per inference
|
|
52
|
+
- **runs**: Number of repeated runs for averaging
|
|
53
|
+
- **warmup**: Initial runs ignored to stabilize measurements
|
|
54
|
+
|
|
55
|
+
### Methods
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
benchmark(model, name, input_shape=(3, 224, 224), framework="pytorch")
|
|
59
|
+
```
|
|
60
|
+
Returns a dict with:
|
|
61
|
+
`{Model, Framework, Device, Latency_ms, Throughput_sps, Memory_MB, Params_M}`
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
summary() # Styled pandas DataFrame (stronger colors on white bg)
|
|
65
|
+
visualize(metric_x="Latency_ms", metric_y="Throughput_sps") # Scatter with improved legend/layout
|
|
66
|
+
visualize_all() # Returns dict of figures: scatter, throughput_bar, latency_bar, memory_bar
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Dependencies
|
|
70
|
+
|
|
71
|
+
- **torch**: PyTorch backend
|
|
72
|
+
- **tensorflow**: TensorFlow backend
|
|
73
|
+
- **plotly**: Interactive plotting
|
|
74
|
+
- **pandas**: Data handling and summary display
|
|
75
|
+
- **psutil**: Memory usage monitoring
|
|
76
|
+
|
|
77
|
+
## Internal Workflow
|
|
78
|
+
|
|
79
|
+
1. Warmup phase to stabilize performance
|
|
80
|
+
2. Timed inference loop across `runs`
|
|
81
|
+
3. Metrics: latency (ms/inference), throughput (samples/sec)
|
|
82
|
+
4. Memory estimation (GPU peak or process RSS)
|
|
83
|
+
5. Results aggregated into a DataFrame and visualized
|
|
84
|
+
6. Memory estimation: reports `Memory_Bytes`, `Memory_MB`, and a humanized `Memory`
|
|
85
|
+
|
|
86
|
+
## Use Cases
|
|
87
|
+
|
|
88
|
+
- **Research**: Compare architectures across frameworks
|
|
89
|
+
- **Development**: Optimize inference pipelines
|
|
90
|
+
- **Education**: Demonstrate benchmarking concepts
|
|
91
|
+
- **Model Selection**: Speed vs efficiency trade-offs
|
|
92
|
+
|
|
93
|
+
## Roadmap
|
|
94
|
+
|
|
95
|
+
- FLOPs estimation
|
|
96
|
+
- Power consumption metrics
|
|
97
|
+
- JAX and ONNX Runtime support
|
|
98
|
+
- Export results to CSV/JSON
|
|
99
|
+
- Leaderboard UI for reproducibility
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
|
104
|
+
|
|
105
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "torchbenchx"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Unified Deep Learning Benchmark Library for PyTorch and TensorFlow"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Syeed Mohd Saquib", email = "syeed.saquib@example.com" }
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"torch>=2.0",
|
|
17
|
+
"tensorflow>=2.12",
|
|
18
|
+
"plotly",
|
|
19
|
+
"pandas",
|
|
20
|
+
"numpy",
|
|
21
|
+
"psutil"
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["."]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://pypi.org/project/torchbenchx/"
|
|
30
|
+
Repository = "https://github.com/syeedsaquib/torchbenchx"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["torchbenchx"]
|
|
34
|
+
|
|
35
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
setup(
|
|
6
|
+
name="torchbenchx",
|
|
7
|
+
version="0.1.0",
|
|
8
|
+
author="Syeed Mohd Saquib",
|
|
9
|
+
author_email="syeedmsaquib@example.com",
|
|
10
|
+
description="Unified Deep Learning Benchmark Library for PyTorch and TensorFlow",
|
|
11
|
+
long_description = Path("README.md").read_text(encoding="utf-8"),
|
|
12
|
+
long_description_content_type="text/markdown",
|
|
13
|
+
url="https://github.com/syeedsaquib/torchbenchx",
|
|
14
|
+
packages=find_packages(),
|
|
15
|
+
include_package_data=True,
|
|
16
|
+
install_requires=[
|
|
17
|
+
"torch",
|
|
18
|
+
"torchvision",
|
|
19
|
+
"tensorflow",
|
|
20
|
+
"plotly",
|
|
21
|
+
"pandas",
|
|
22
|
+
"psutil"
|
|
23
|
+
],
|
|
24
|
+
classifiers=[
|
|
25
|
+
"Development Status :: 4 - Beta",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"Topic :: Software Development :: Libraries",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"License :: OSI Approved :: MIT License",
|
|
30
|
+
"Operating System :: OS Independent",
|
|
31
|
+
],
|
|
32
|
+
python_requires=">=3.8",
|
|
33
|
+
)
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
6
|
+
|
|
7
|
+
import psutil
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import plotly.express as px
|
|
12
|
+
except Exception: # pragma: no cover - optional at runtime
|
|
13
|
+
px = None # type: ignore
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class _Result:
|
|
18
|
+
Model: str
|
|
19
|
+
Framework: str
|
|
20
|
+
Device: str
|
|
21
|
+
Latency_ms: float
|
|
22
|
+
Throughput_sps: float
|
|
23
|
+
Memory_MB: float
|
|
24
|
+
Memory_Bytes: int
|
|
25
|
+
Params_M: float
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BenchX:
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
device: Optional[str] = None,
|
|
32
|
+
batch_size: int = 16,
|
|
33
|
+
runs: int = 30,
|
|
34
|
+
warmup: int = 10,
|
|
35
|
+
) -> None:
|
|
36
|
+
self._device_pref = device
|
|
37
|
+
self.batch_size = int(batch_size)
|
|
38
|
+
self.runs = int(runs)
|
|
39
|
+
self.warmup = int(warmup)
|
|
40
|
+
self._results: List[_Result] = []
|
|
41
|
+
|
|
42
|
+
# ----------------------------- Public API ----------------------------- #
|
|
43
|
+
def benchmark(
|
|
44
|
+
self,
|
|
45
|
+
model: Any,
|
|
46
|
+
name: str,
|
|
47
|
+
input_shape: Sequence[int] = (3, 224, 224),
|
|
48
|
+
framework: str = "pytorch",
|
|
49
|
+
) -> Dict[str, Any]:
|
|
50
|
+
framework_lc = framework.lower()
|
|
51
|
+
if framework_lc not in ("pytorch", "tensorflow"):
|
|
52
|
+
raise ValueError("framework must be 'pytorch' or 'tensorflow'")
|
|
53
|
+
|
|
54
|
+
if framework_lc == "pytorch":
|
|
55
|
+
result = self._benchmark_torch(model, name, tuple(input_shape))
|
|
56
|
+
else:
|
|
57
|
+
result = self._benchmark_tf(model, name, tuple(input_shape))
|
|
58
|
+
|
|
59
|
+
self._results.append(_Result(**result))
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
def summary(self) -> pd.io.formats.style.Styler:
|
|
63
|
+
df = pd.DataFrame([r.__dict__ for r in self._results])
|
|
64
|
+
if df.empty:
|
|
65
|
+
return pd.DataFrame(columns=[
|
|
66
|
+
"Model", "Framework", "Device", "Latency_ms",
|
|
67
|
+
"Throughput_sps", "Memory_MB", "Memory_Bytes", "Params_M",
|
|
68
|
+
]).style
|
|
69
|
+
|
|
70
|
+
# Add humanized memory column for readability
|
|
71
|
+
df["Memory"] = df["Memory_Bytes"].map(self._humanize_bytes)
|
|
72
|
+
df_sorted = df.sort_values(by=["Throughput_sps"], ascending=False, ignore_index=True)
|
|
73
|
+
|
|
74
|
+
styler = df_sorted.style.format({
|
|
75
|
+
"Latency_ms": "{:.2f}",
|
|
76
|
+
"Throughput_sps": "{:.0f}",
|
|
77
|
+
"Memory_MB": "{:.1f}",
|
|
78
|
+
"Params_M": "{:.2f}",
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
# Highlight best throughput
|
|
82
|
+
if len(df_sorted) > 0:
|
|
83
|
+
max_idx = df_sorted["Throughput_sps"].idxmax()
|
|
84
|
+
def _highlight_best(row: pd.Series) -> List[str]:
|
|
85
|
+
return [
|
|
86
|
+
"background-color: #e6ffed; font-weight: 600;" if row.name == max_idx else ""
|
|
87
|
+
] * len(row)
|
|
88
|
+
styler = styler.apply(_highlight_best, axis=1)
|
|
89
|
+
|
|
90
|
+
# Stronger, more visible color accents on white
|
|
91
|
+
styler = styler.background_gradient(subset=["Latency_ms"], cmap="Reds_r")
|
|
92
|
+
styler = styler.background_gradient(subset=["Throughput_sps"], cmap="Greens")
|
|
93
|
+
styler = styler.background_gradient(subset=["Memory_MB"], cmap="Purples")
|
|
94
|
+
|
|
95
|
+
# Subtle borders for a neat table look
|
|
96
|
+
styler = styler.set_table_styles([
|
|
97
|
+
{"selector": "table", "props": [
|
|
98
|
+
("border-collapse", "separate"), ("border-spacing", "0 8px"), ("background", "#ffffff")
|
|
99
|
+
]},
|
|
100
|
+
{"selector": "th", "props": [("background", "#f0f4ff"), ("font-weight", "700"), ("color", "#222")]} ,
|
|
101
|
+
{"selector": "td", "props": [("border", "1px solid #ddd"), ("padding", "8px 12px"), ("color", "#111")]},
|
|
102
|
+
])
|
|
103
|
+
try:
|
|
104
|
+
styler = styler.hide_columns(["Memory_Bytes"]) # keep the raw bytes hidden but available
|
|
105
|
+
except Exception:
|
|
106
|
+
pass
|
|
107
|
+
return styler
|
|
108
|
+
|
|
109
|
+
def visualize(
|
|
110
|
+
self,
|
|
111
|
+
metric_x: str = "Latency_ms",
|
|
112
|
+
metric_y: str = "Throughput_sps",
|
|
113
|
+
) -> Any:
|
|
114
|
+
if px is None:
|
|
115
|
+
raise RuntimeError("plotly is not installed. Please `pip install plotly`. ")
|
|
116
|
+
|
|
117
|
+
df = pd.DataFrame([r.__dict__ for r in self._results])
|
|
118
|
+
if df.empty:
|
|
119
|
+
raise RuntimeError("No results to visualize. Run benchmark() first.")
|
|
120
|
+
|
|
121
|
+
fig = px.scatter(
|
|
122
|
+
df,
|
|
123
|
+
x=metric_x,
|
|
124
|
+
y=metric_y,
|
|
125
|
+
color="Framework",
|
|
126
|
+
size="Params_M",
|
|
127
|
+
hover_data=["Model", "Device", "Memory_MB"],
|
|
128
|
+
title="torchbenchx — Performance Comparison",
|
|
129
|
+
)
|
|
130
|
+
fig.update_layout(legend_title_text="Framework")
|
|
131
|
+
fig.update_traces(marker=dict(opacity=0.85, line=dict(width=1, color="#ffffff")))
|
|
132
|
+
return fig
|
|
133
|
+
|
|
134
|
+
# ----------------------------- Backends ------------------------------ #
|
|
135
|
+
def _benchmark_torch(self, model: Any, name: str, input_shape: Tuple[int, ...]) -> Dict[str, Any]:
|
|
136
|
+
import math
|
|
137
|
+
import numpy as np
|
|
138
|
+
try:
|
|
139
|
+
import torch
|
|
140
|
+
except Exception as exc: # pragma: no cover
|
|
141
|
+
raise RuntimeError("PyTorch is required for framework='pytorch'") from exc
|
|
142
|
+
|
|
143
|
+
device = self._resolve_torch_device()
|
|
144
|
+
model = model.to(device)
|
|
145
|
+
model.eval()
|
|
146
|
+
|
|
147
|
+
batch = self.batch_size
|
|
148
|
+
c, h, w = input_shape
|
|
149
|
+
dummy = torch.randn(batch, c, h, w, device=device)
|
|
150
|
+
|
|
151
|
+
# Parameter count (millions) and bytes
|
|
152
|
+
param_elems = sum(p.numel() for p in model.parameters())
|
|
153
|
+
params_m = param_elems / 1_000_000.0
|
|
154
|
+
param_bytes = sum(p.numel() * p.element_size() for p in model.parameters())
|
|
155
|
+
|
|
156
|
+
# Memory tracking (GPU peak if CUDA, else process RSS delta)
|
|
157
|
+
process = psutil.Process()
|
|
158
|
+
start_rss = process.memory_info().rss
|
|
159
|
+
peak_mb_gpu = 0.0
|
|
160
|
+
if device.type == "cuda":
|
|
161
|
+
torch.cuda.reset_peak_memory_stats(device)
|
|
162
|
+
|
|
163
|
+
# Warmup
|
|
164
|
+
with torch.no_grad():
|
|
165
|
+
for _ in range(self.warmup):
|
|
166
|
+
_ = model(dummy)
|
|
167
|
+
if device.type == "cuda":
|
|
168
|
+
torch.cuda.synchronize(device)
|
|
169
|
+
|
|
170
|
+
# Timed runs
|
|
171
|
+
start = time.perf_counter()
|
|
172
|
+
with torch.no_grad():
|
|
173
|
+
for _ in range(self.runs):
|
|
174
|
+
_ = model(dummy)
|
|
175
|
+
if device.type == "cuda":
|
|
176
|
+
torch.cuda.synchronize(device)
|
|
177
|
+
total = time.perf_counter() - start
|
|
178
|
+
|
|
179
|
+
if device.type == "cuda":
|
|
180
|
+
peak_bytes = torch.cuda.max_memory_allocated(device)
|
|
181
|
+
peak_mb_gpu = peak_bytes / (1024 ** 2)
|
|
182
|
+
end_rss = process.memory_info().rss
|
|
183
|
+
delta_mb_cpu = max(0.0, (end_rss - start_rss) / (1024 ** 2))
|
|
184
|
+
# Estimated activation memory from input (rough, but non-zero and comparable)
|
|
185
|
+
activation_bytes = self.batch_size * c * h * w * 4 # float32
|
|
186
|
+
est_bytes = max(int(param_bytes + activation_bytes), 0)
|
|
187
|
+
# Prefer GPU peak if available, else use estimate (avoid zeroes)
|
|
188
|
+
mem_bytes = int(peak_mb_gpu * (1024 ** 2)) if device.type == "cuda" and peak_mb_gpu > 0 else max(int(delta_mb_cpu * (1024 ** 2)), est_bytes)
|
|
189
|
+
mem_mb = mem_bytes / (1024 ** 2)
|
|
190
|
+
|
|
191
|
+
latency_ms = (total / self.runs) * 1000.0
|
|
192
|
+
throughput_sps = (batch * self.runs) / max(total, 1e-9)
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
"Model": name,
|
|
196
|
+
"Framework": "PyTorch",
|
|
197
|
+
"Device": str(device),
|
|
198
|
+
"Latency_ms": float(latency_ms),
|
|
199
|
+
"Throughput_sps": float(throughput_sps),
|
|
200
|
+
"Memory_MB": float(mem_mb),
|
|
201
|
+
"Memory_Bytes": int(mem_bytes),
|
|
202
|
+
"Params_M": float(params_m),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
def _benchmark_tf(self, model: Any, name: str, input_shape: Tuple[int, ...]) -> Dict[str, Any]:
|
|
206
|
+
import numpy as np
|
|
207
|
+
try:
|
|
208
|
+
import tensorflow as tf
|
|
209
|
+
except Exception as exc: # pragma: no cover
|
|
210
|
+
raise RuntimeError("TensorFlow is required for framework='tensorflow'") from exc
|
|
211
|
+
|
|
212
|
+
# TensorFlow typically uses channels-last
|
|
213
|
+
if len(input_shape) != 3:
|
|
214
|
+
raise ValueError("input_shape must be a 3-tuple for images")
|
|
215
|
+
c, h, w = input_shape
|
|
216
|
+
hwc = (h, w, c)
|
|
217
|
+
|
|
218
|
+
batch = self.batch_size
|
|
219
|
+
dummy_np = np.random.randn(batch, *hwc).astype("float32")
|
|
220
|
+
dummy = tf.convert_to_tensor(dummy_np)
|
|
221
|
+
|
|
222
|
+
# Ensure model is built
|
|
223
|
+
_ = model(dummy, training=False)
|
|
224
|
+
|
|
225
|
+
# Parameter count (millions) and bytes
|
|
226
|
+
try:
|
|
227
|
+
params = model.count_params()
|
|
228
|
+
except Exception:
|
|
229
|
+
params = sum(int(v.shape.num_elements() or 0) for v in model.trainable_variables)
|
|
230
|
+
params_m = params / 1_000_000.0
|
|
231
|
+
param_bytes = params * 4 # assume float32
|
|
232
|
+
|
|
233
|
+
# Device string
|
|
234
|
+
device = "GPU" if tf.config.list_physical_devices("GPU") else "CPU"
|
|
235
|
+
|
|
236
|
+
process = psutil.Process()
|
|
237
|
+
start_rss = process.memory_info().rss
|
|
238
|
+
|
|
239
|
+
# Warmup
|
|
240
|
+
for _ in range(self.warmup):
|
|
241
|
+
_ = model(dummy, training=False)
|
|
242
|
+
# Best-effort sync using .numpy() materialization
|
|
243
|
+
_ = _.numpy() if hasattr(_, "numpy") else _
|
|
244
|
+
|
|
245
|
+
# Timed runs
|
|
246
|
+
start = time.perf_counter()
|
|
247
|
+
for _ in range(self.runs):
|
|
248
|
+
out = model(dummy, training=False)
|
|
249
|
+
_ = out.numpy() if hasattr(out, "numpy") else out
|
|
250
|
+
total = time.perf_counter() - start
|
|
251
|
+
|
|
252
|
+
end_rss = process.memory_info().rss
|
|
253
|
+
delta_bytes = max(0, end_rss - start_rss)
|
|
254
|
+
activation_bytes = self.batch_size * h * w * c * 4 # float32
|
|
255
|
+
est_bytes = max(int(param_bytes + activation_bytes), 0)
|
|
256
|
+
mem_bytes = max(delta_bytes, est_bytes)
|
|
257
|
+
mem_mb = mem_bytes / (1024 ** 2)
|
|
258
|
+
|
|
259
|
+
latency_ms = (total / self.runs) * 1000.0
|
|
260
|
+
throughput_sps = (batch * self.runs) / max(total, 1e-9)
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
"Model": name,
|
|
264
|
+
"Framework": "TensorFlow",
|
|
265
|
+
"Device": device.lower(),
|
|
266
|
+
"Latency_ms": float(latency_ms),
|
|
267
|
+
"Throughput_sps": float(throughput_sps),
|
|
268
|
+
"Memory_MB": float(mem_mb),
|
|
269
|
+
"Memory_Bytes": int(mem_bytes),
|
|
270
|
+
"Params_M": float(params_m),
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# ----------------------------- Utils --------------------------------- #
|
|
274
|
+
def _resolve_torch_device(self):
|
|
275
|
+
try:
|
|
276
|
+
import torch
|
|
277
|
+
except Exception as exc: # pragma: no cover
|
|
278
|
+
raise RuntimeError("PyTorch is required for framework='pytorch'") from exc
|
|
279
|
+
|
|
280
|
+
if self._device_pref is not None:
|
|
281
|
+
return torch.device(self._device_pref)
|
|
282
|
+
return torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
283
|
+
|
|
284
|
+
def _humanize_bytes(self, n: int) -> str:
|
|
285
|
+
units = ["B", "KB", "MB", "GB", "TB"]
|
|
286
|
+
x = float(n)
|
|
287
|
+
idx = 0
|
|
288
|
+
while x >= 1024.0 and idx < len(units) - 1:
|
|
289
|
+
x /= 1024.0
|
|
290
|
+
idx += 1
|
|
291
|
+
if idx == 0:
|
|
292
|
+
return f"{int(x)} {units[idx]}"
|
|
293
|
+
return f"{x:.2f} {units[idx]}"
|
|
294
|
+
|
|
295
|
+
# -------------------------- Enhanced Visuals ------------------------- #
|
|
296
|
+
def visualize(
|
|
297
|
+
self,
|
|
298
|
+
metric_x: str = "Latency_ms",
|
|
299
|
+
metric_y: str = "Throughput_sps",
|
|
300
|
+
) -> Any:
|
|
301
|
+
if px is None:
|
|
302
|
+
raise RuntimeError("plotly is not installed. Please `pip install plotly`. ")
|
|
303
|
+
|
|
304
|
+
df = pd.DataFrame([r.__dict__ for r in self._results])
|
|
305
|
+
if df.empty:
|
|
306
|
+
raise RuntimeError("No results to visualize. Run benchmark() first.")
|
|
307
|
+
|
|
308
|
+
fig = px.scatter(
|
|
309
|
+
df,
|
|
310
|
+
x=metric_x,
|
|
311
|
+
y=metric_y,
|
|
312
|
+
color="Framework",
|
|
313
|
+
size="Params_M",
|
|
314
|
+
hover_data=["Model", "Device", "Memory_MB"],
|
|
315
|
+
title="torchbenchx — Performance Comparison",
|
|
316
|
+
)
|
|
317
|
+
fig.update_layout(
|
|
318
|
+
legend_title_text="Framework",
|
|
319
|
+
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
|
|
320
|
+
title=dict(font=dict(size=20, color="#111")),
|
|
321
|
+
xaxis_title=dict(text=metric_x.replace("_", " "), font=dict(size=14)),
|
|
322
|
+
yaxis_title=dict(text=metric_y.replace("_", " "), font=dict(size=14)),
|
|
323
|
+
plot_bgcolor="#fff",
|
|
324
|
+
paper_bgcolor="#fff",
|
|
325
|
+
)
|
|
326
|
+
fig.update_xaxes(showgrid=True, gridcolor="#eee")
|
|
327
|
+
fig.update_yaxes(showgrid=True, gridcolor="#eee")
|
|
328
|
+
fig.update_traces(marker=dict(opacity=0.9, line=dict(width=1, color="#ffffff")))
|
|
329
|
+
return fig
|
|
330
|
+
|
|
331
|
+
def visualize_all(self) -> Dict[str, Any]:
|
|
332
|
+
if px is None:
|
|
333
|
+
raise RuntimeError("plotly is not installed. Please `pip install plotly`. ")
|
|
334
|
+
df = pd.DataFrame([r.__dict__ for r in self._results])
|
|
335
|
+
if df.empty:
|
|
336
|
+
raise RuntimeError("No results to visualize. Run benchmark() first.")
|
|
337
|
+
|
|
338
|
+
figs: Dict[str, Any] = {}
|
|
339
|
+
# Scatter throughput vs latency
|
|
340
|
+
figs["scatter"] = self.visualize("Latency_ms", "Throughput_sps")
|
|
341
|
+
|
|
342
|
+
# Bar: Throughput by model
|
|
343
|
+
figs["throughput_bar"] = px.bar(
|
|
344
|
+
df.sort_values("Throughput_sps", ascending=False),
|
|
345
|
+
x="Model",
|
|
346
|
+
y="Throughput_sps",
|
|
347
|
+
color="Framework",
|
|
348
|
+
title="Throughput (samples/sec) by Model",
|
|
349
|
+
)
|
|
350
|
+
figs["throughput_bar"].update_layout(legend=dict(orientation="h", y=1.02, x=1, xanchor="right"))
|
|
351
|
+
figs["throughput_bar"].update_yaxes(gridcolor="#eee")
|
|
352
|
+
|
|
353
|
+
# Bar: Latency by model (lower is better)
|
|
354
|
+
figs["latency_bar"] = px.bar(
|
|
355
|
+
df.sort_values("Latency_ms", ascending=True),
|
|
356
|
+
x="Model",
|
|
357
|
+
y="Latency_ms",
|
|
358
|
+
color="Framework",
|
|
359
|
+
title="Latency (ms per inference) by Model",
|
|
360
|
+
)
|
|
361
|
+
figs["latency_bar"].update_layout(legend=dict(orientation="h", y=1.02, x=1, xanchor="right"))
|
|
362
|
+
figs["latency_bar"].update_yaxes(gridcolor="#eee")
|
|
363
|
+
|
|
364
|
+
# Bar: Memory by model (MB)
|
|
365
|
+
figs["memory_bar"] = px.bar(
|
|
366
|
+
df.sort_values("Memory_MB", ascending=True),
|
|
367
|
+
x="Model",
|
|
368
|
+
y="Memory_MB",
|
|
369
|
+
color="Framework",
|
|
370
|
+
title="Estimated Memory Usage (MB) by Model",
|
|
371
|
+
)
|
|
372
|
+
figs["memory_bar"].update_layout(legend=dict(orientation="h", y=1.02, x=1, xanchor="right"))
|
|
373
|
+
figs["memory_bar"].update_yaxes(gridcolor="#eee")
|
|
374
|
+
return figs
|
|
375
|
+
|
|
376
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: torchbenchx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unified Deep Learning Benchmark Library for PyTorch and TensorFlow
|
|
5
|
+
Home-page: https://github.com/syeedsaquib/torchbenchx
|
|
6
|
+
Author: Syeed Mohd Saquib
|
|
7
|
+
Author-email: Syeed Mohd Saquib <syeed.saquib@example.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://pypi.org/project/torchbenchx/
|
|
10
|
+
Project-URL: Repository, https://github.com/syeedsaquib/torchbenchx
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: torch>=2.0
|
|
15
|
+
Requires-Dist: tensorflow>=2.12
|
|
16
|
+
Requires-Dist: plotly
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: psutil
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-python
|
|
24
|
+
|
|
25
|
+
# torchbenchx — Unified Deep Learning Benchmark Library
|
|
26
|
+
|
|
27
|
+
Benchmark. Compare. Visualize.
|
|
28
|
+
|
|
29
|
+
`torchbenchx` is a framework-agnostic deep learning benchmarking library that measures latency, throughput, memory usage, and parameter count of models from both PyTorch and TensorFlow — all inside your Jupyter Notebook, with no servers or dashboards required.
|
|
30
|
+
|
|
31
|
+
It’s designed for researchers, developers, and AI enthusiasts who want quick, reliable model comparisons with interactive visualizations.
|
|
32
|
+
|
|
33
|
+
## ⚙️ Key Features
|
|
34
|
+
|
|
35
|
+
- **Dual Framework Support**: Benchmark models from both PyTorch and TensorFlow using one unified API.
|
|
36
|
+
- **Latency & Throughput Measurement**: Calculates per-inference latency and samples-per-second throughput.
|
|
37
|
+
- **Model Parameters & Memory Usage**: Automatically estimates memory footprint and parameter count.
|
|
38
|
+
- **Device Awareness**: Detects and runs on CPU or GPU automatically.
|
|
39
|
+
- **Notebook-native Visualization**: Generates Plotly scatter plots of performance metrics directly in Jupyter/Colab.
|
|
40
|
+
- **Minimal Dependencies**: Lightweight and ready to use.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install torchbenchx
|
|
46
|
+
# Backends and viz libs
|
|
47
|
+
pip install torch torchvision tensorflow plotly pandas psutil
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Optional: install `notebook` or `jupyterlab` if using outside Colab.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from torchbenchx import BenchX
|
|
56
|
+
import torchvision.models as tv_models
|
|
57
|
+
import tensorflow as tf
|
|
58
|
+
|
|
59
|
+
bench = BenchX(batch_size=16, runs=30)
|
|
60
|
+
|
|
61
|
+
bench.benchmark(tv_models.resnet18(weights=None), "ResNet18", framework="pytorch")
|
|
62
|
+
bench.benchmark(tf.keras.applications.MobileNetV3Small(), "MobileNetV3", framework="tensorflow")
|
|
63
|
+
|
|
64
|
+
bench.summary() # Styled table
|
|
65
|
+
bench.visualize() # Plotly scatter
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Core Class: BenchX
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
BenchX(device=None, batch_size=16, runs=30, warmup=10)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- **device**: "cuda" or "cpu" — auto-detected if not set (for PyTorch)
|
|
75
|
+
- **batch_size**: Number of samples per inference
|
|
76
|
+
- **runs**: Number of repeated runs for averaging
|
|
77
|
+
- **warmup**: Initial runs ignored to stabilize measurements
|
|
78
|
+
|
|
79
|
+
### Methods
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
benchmark(model, name, input_shape=(3, 224, 224), framework="pytorch")
|
|
83
|
+
```
|
|
84
|
+
Returns a dict with:
|
|
85
|
+
`{Model, Framework, Device, Latency_ms, Throughput_sps, Memory_MB, Params_M}`
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
summary() # Styled pandas DataFrame (stronger colors on white bg)
|
|
89
|
+
visualize(metric_x="Latency_ms", metric_y="Throughput_sps") # Scatter with improved legend/layout
|
|
90
|
+
visualize_all() # Returns dict of figures: scatter, throughput_bar, latency_bar, memory_bar
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Dependencies
|
|
94
|
+
|
|
95
|
+
- **torch**: PyTorch backend
|
|
96
|
+
- **tensorflow**: TensorFlow backend
|
|
97
|
+
- **plotly**: Interactive plotting
|
|
98
|
+
- **pandas**: Data handling and summary display
|
|
99
|
+
- **psutil**: Memory usage monitoring
|
|
100
|
+
|
|
101
|
+
## Internal Workflow
|
|
102
|
+
|
|
103
|
+
1. Warmup phase to stabilize performance
|
|
104
|
+
2. Timed inference loop across `runs`
|
|
105
|
+
3. Metrics: latency (ms/inference), throughput (samples/sec)
|
|
106
|
+
4. Memory estimation (GPU peak or process RSS)
|
|
107
|
+
5. Results aggregated into a DataFrame and visualized
|
|
108
|
+
6. Memory estimation: reports `Memory_Bytes`, `Memory_MB`, and a humanized `Memory`
|
|
109
|
+
|
|
110
|
+
## Use Cases
|
|
111
|
+
|
|
112
|
+
- **Research**: Compare architectures across frameworks
|
|
113
|
+
- **Development**: Optimize inference pipelines
|
|
114
|
+
- **Education**: Demonstrate benchmarking concepts
|
|
115
|
+
- **Model Selection**: Speed vs efficiency trade-offs
|
|
116
|
+
|
|
117
|
+
## Roadmap
|
|
118
|
+
|
|
119
|
+
- FLOPs estimation
|
|
120
|
+
- Power consumption metrics
|
|
121
|
+
- JAX and ONNX Runtime support
|
|
122
|
+
- Export results to CSV/JSON
|
|
123
|
+
- Leaderboard UI for reproducibility
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
|
128
|
+
|
|
129
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.py
|
|
6
|
+
torchbenchx/__init__.py
|
|
7
|
+
torchbenchx/benchx.py
|
|
8
|
+
torchbenchx.egg-info/PKG-INFO
|
|
9
|
+
torchbenchx.egg-info/SOURCES.txt
|
|
10
|
+
torchbenchx.egg-info/dependency_links.txt
|
|
11
|
+
torchbenchx.egg-info/requires.txt
|
|
12
|
+
torchbenchx.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
torchbenchx
|