autohetero 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.
- autohetero-0.1.0/LICENSE +21 -0
- autohetero-0.1.0/PKG-INFO +254 -0
- autohetero-0.1.0/README.md +229 -0
- autohetero-0.1.0/autohetero/__init__.py +15 -0
- autohetero-0.1.0/autohetero/calibrate.py +242 -0
- autohetero-0.1.0/autohetero/devices.py +88 -0
- autohetero-0.1.0/autohetero/dispatch.py +177 -0
- autohetero-0.1.0/autohetero/executor.py +112 -0
- autohetero-0.1.0/autohetero/features.py +51 -0
- autohetero-0.1.0/autohetero/history.py +182 -0
- autohetero-0.1.0/autohetero/monitor.py +83 -0
- autohetero-0.1.0/autohetero/ops/__init__.py +33 -0
- autohetero-0.1.0/autohetero/ops/analytics.py +23 -0
- autohetero-0.1.0/autohetero/ops/image.py +43 -0
- autohetero-0.1.0/autohetero/ops/inputs.py +42 -0
- autohetero-0.1.0/autohetero/ops/models.py +39 -0
- autohetero-0.1.0/autohetero/ops/science.py +15 -0
- autohetero-0.1.0/autohetero/predictor.py +146 -0
- autohetero-0.1.0/autohetero/report.py +45 -0
- autohetero-0.1.0/autohetero.egg-info/PKG-INFO +254 -0
- autohetero-0.1.0/autohetero.egg-info/SOURCES.txt +45 -0
- autohetero-0.1.0/autohetero.egg-info/dependency_links.txt +1 -0
- autohetero-0.1.0/autohetero.egg-info/requires.txt +13 -0
- autohetero-0.1.0/autohetero.egg-info/top_level.txt +1 -0
- autohetero-0.1.0/pyproject.toml +38 -0
- autohetero-0.1.0/setup.cfg +4 -0
- autohetero-0.1.0/tests/test_calibrate.py +91 -0
- autohetero-0.1.0/tests/test_collect.py +15 -0
- autohetero-0.1.0/tests/test_crossover.py +23 -0
- autohetero-0.1.0/tests/test_demo.py +68 -0
- autohetero-0.1.0/tests/test_devices.py +64 -0
- autohetero-0.1.0/tests/test_dispatch.py +185 -0
- autohetero-0.1.0/tests/test_eval_week5.py +40 -0
- autohetero-0.1.0/tests/test_eval_week8.py +83 -0
- autohetero-0.1.0/tests/test_executor.py +133 -0
- autohetero-0.1.0/tests/test_experiments.py +53 -0
- autohetero-0.1.0/tests/test_features.py +57 -0
- autohetero-0.1.0/tests/test_history.py +132 -0
- autohetero-0.1.0/tests/test_monitor.py +26 -0
- autohetero-0.1.0/tests/test_ops_data.py +41 -0
- autohetero-0.1.0/tests/test_ops_image.py +32 -0
- autohetero-0.1.0/tests/test_ops_models.py +28 -0
- autohetero-0.1.0/tests/test_ops_parity.py +42 -0
- autohetero-0.1.0/tests/test_predictor.py +83 -0
- autohetero-0.1.0/tests/test_report.py +42 -0
- autohetero-0.1.0/tests/test_trials.py +76 -0
- autohetero-0.1.0/tests/test_week6.py +58 -0
autohetero-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SRIVATHSA B S
|
|
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,254 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autohetero
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Self-adaptive, zero-configuration CPU-GPU workload dispatch for Python applications
|
|
5
|
+
Author: SRIVATHSA B S
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/VARITHSA/autohetero
|
|
8
|
+
Project-URL: Repository, https://github.com/VARITHSA/autohetero
|
|
9
|
+
Project-URL: Issues, https://github.com/VARITHSA/autohetero/issues
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: torch>=2.1
|
|
14
|
+
Requires-Dist: torchvision
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: scikit-learn
|
|
17
|
+
Requires-Dist: psutil
|
|
18
|
+
Requires-Dist: nvidia-ml-py>=12
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest; extra == "dev"
|
|
21
|
+
Requires-Dist: matplotlib; extra == "dev"
|
|
22
|
+
Provides-Extra: demo
|
|
23
|
+
Requires-Dist: streamlit; extra == "demo"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# AutoHetero
|
|
27
|
+
|
|
28
|
+
  
|
|
29
|
+
|
|
30
|
+
**AutoHetero runs each PyTorch call on the CPU, the GPU, or both — whichever is fastest.**
|
|
31
|
+
|
|
32
|
+
A GPU isn't always the faster choice. For small inputs, moving data to the GPU costs more than the work itself, and a GPU that's busy with another job can end up slower than the CPU. I built AutoHetero to make that decision automatically: it benchmarks your machine once, predicts the fastest device for every call, and shifts work to the CPU when the GPU falls behind.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **One line to enable.** No configuration, no `.to("cuda")`.
|
|
37
|
+
- **Tensors or NumPy.** Results come back in the same type and on the same device you passed in.
|
|
38
|
+
- **Hybrid mode.** Large batches can be split across the CPU and GPU at the same time.
|
|
39
|
+
- **Adapts while running.** If the GPU slows down mid-call, the rest of the work is rebalanced.
|
|
40
|
+
- **Safe fallback.** GPU errors (like running out of memory) rerun the call on the CPU instead of crashing.
|
|
41
|
+
- **Transparent.** A short log line for every decision, and `autohetero.report()` shows what ran where.
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Python 3.11+ and PyTorch 2.x
|
|
46
|
+
- An NVIDIA GPU with a CUDA build of PyTorch. Without one, AutoHetero still works and runs everything on the CPU.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
**To use it in your own project**, install it from PyPI into that project's environment:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install autohetero
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**To run the demos, tests and benchmarks**, clone the repository:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git clone https://github.com/VARITHSA/autohetero.git
|
|
60
|
+
cd autohetero
|
|
61
|
+
pip install -e . # add ".[dev]" for tests, ".[demo]" for the demo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Check that PyTorch can see your GPU (it should print `True`):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python -c "import torch; print(torch.cuda.is_available())"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If it prints `False`, install the CUDA build of PyTorch for your driver from [pytorch.org](https://pytorch.org/get-started/locally/).
|
|
71
|
+
|
|
72
|
+
## Try it
|
|
73
|
+
|
|
74
|
+
From the cloned repository:
|
|
75
|
+
|
|
76
|
+
1. **With and without, side by side.** This runs the same image pipeline as plain PyTorch on the CPU, by hand on the GPU, and with AutoHetero, checks that all three give the same answer, and prints their times:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
python demo/compare.py
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
2. **The demo app.** Install the demo extra, then start the app; it opens in your browser:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install -e ".[demo]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
streamlit run demo/app.py
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pick a batch size and resolution and click **Run**. Turn on **GPU stress** to watch AutoHetero move work to the CPU. The **playground** page in the sidebar runs any built-in op at any size.
|
|
93
|
+
|
|
94
|
+
3. **The measured results.** Open `demo/showcase.ipynb` in Jupyter (`pip install jupyter` first). It reads the results from my RTX 3050 runs, so it works on a machine without a GPU.
|
|
95
|
+
|
|
96
|
+
## Using it in your code
|
|
97
|
+
|
|
98
|
+
1. Import AutoHetero and call `autohetero.enable()` once, at the start of your program.
|
|
99
|
+
2. Use the built-in ops (`autohetero.ops.*`), or put `@autohetero.auto` on your own functions.
|
|
100
|
+
3. Keep your data on the CPU, as usual. Don't call `.to("cuda")`; AutoHetero moves the data and brings the result back.
|
|
101
|
+
4. Call `autohetero.report()` at the end to see where each call ran.
|
|
102
|
+
|
|
103
|
+
**Built-in ops** — one line:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import torch
|
|
107
|
+
import autohetero
|
|
108
|
+
|
|
109
|
+
autohetero.enable() # the one line: built-in ops now run where they're fastest
|
|
110
|
+
|
|
111
|
+
images = torch.rand(16, 3, 720, 1280) # on the CPU, as usual
|
|
112
|
+
edges = autohetero.ops.sobel(autohetero.ops.blur(images)) # each op is dispatched; the result comes back on the CPU
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Your own functions** — one decorator:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import numpy as np
|
|
119
|
+
import torch
|
|
120
|
+
import autohetero
|
|
121
|
+
|
|
122
|
+
@autohetero.auto(splittable=True)
|
|
123
|
+
def normalize(x):
|
|
124
|
+
# x arrives as a torch tensor on whichever device AutoHetero chose
|
|
125
|
+
return (x - x.mean(dim=1, keepdim=True)) / x.std(dim=1, keepdim=True)
|
|
126
|
+
|
|
127
|
+
out = normalize(np.random.rand(10_000, 512).astype(np.float32)) # numpy in -> numpy out
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Models work too. Pass them in as arguments:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import torchvision
|
|
134
|
+
|
|
135
|
+
model = torchvision.models.resnet18(weights=None).eval()
|
|
136
|
+
|
|
137
|
+
@autohetero.auto(splittable=True)
|
|
138
|
+
def classify(images, model):
|
|
139
|
+
with torch.no_grad():
|
|
140
|
+
return model(images).argmax(1)
|
|
141
|
+
|
|
142
|
+
batch = torch.rand(64, 3, 224, 224)
|
|
143
|
+
labels = classify(batch, model)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Manual override**, when you know better:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
with autohetero.force("cpu"): # or "gpu", or "hybrid"
|
|
150
|
+
labels = classify(batch, model)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**See what happened:**
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
autohetero.report()
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
function calls modes time saved vs GPU (est.) fallbacks switches
|
|
161
|
+
-------- ----- --------------- -------- ------------------- --------- --------
|
|
162
|
+
blur 3 GPU 3 91.2 ms 12.0 ms 0 0
|
|
163
|
+
classify 2 HYBRID 1, GPU 1 612.0 ms 48.0 ms 0 1
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The first call on a new machine runs a one-time calibration (about 30 seconds).
|
|
167
|
+
|
|
168
|
+
## How it works
|
|
169
|
+
|
|
170
|
+
1. **Calibrate.** On first use, AutoHetero times its built-in ops on the CPU and GPU and caches the results in `~/.cache/autohetero/`.
|
|
171
|
+
2. **Predict.** A small learned model (random forests) estimates how long each call will take on each device. Your own functions are predicted from their own past runs; the first call measures both devices.
|
|
172
|
+
3. **Run.** Long calls run in chunks. If a chunk takes more than 1.5× longer than expected, the rest of the call is split between the CPU and GPU by their measured speeds.
|
|
173
|
+
4. **Learn.** Every call is logged with its predicted and actual time, so predictions improve over time. Call `autohetero.retrain()` to update the model.
|
|
174
|
+
|
|
175
|
+
The full design is in [DESIGN.md](https://github.com/VARITHSA/autohetero/blob/main/DESIGN.md).
|
|
176
|
+
|
|
177
|
+
## API
|
|
178
|
+
|
|
179
|
+
| Function | What it does |
|
|
180
|
+
|----------|--------------|
|
|
181
|
+
| `autohetero.enable()` | Turns on dispatch for the built-in ops and calibrates the machine (once). |
|
|
182
|
+
| `@autohetero.auto(splittable=False, batch_dim=0)` | Runs every call of a function on the fastest option. Set `splittable=True` only if each output row depends only on the matching input row; that allows chunking and Hybrid mode. |
|
|
183
|
+
| `autohetero.force(mode)` | Context manager: run on `"cpu"`, `"gpu"` or `"hybrid"`. Applies to the current thread only. |
|
|
184
|
+
| `autohetero.report()` | Prints (and returns) what ran where, total time, and the estimated time saved. |
|
|
185
|
+
| `autohetero.retrain()` | Updates the prediction model from the calls recorded so far. |
|
|
186
|
+
|
|
187
|
+
### Tips for `@auto` functions
|
|
188
|
+
|
|
189
|
+
- Use torch operations — inputs arrive as tensors, even if you passed NumPy arrays.
|
|
190
|
+
- Don't modify inputs in place (`x += 1`).
|
|
191
|
+
- Pass models as arguments, not as globals.
|
|
192
|
+
- If the GPU fails, the call reruns on the CPU, so avoid side effects you can't repeat.
|
|
193
|
+
|
|
194
|
+
## Built-in ops
|
|
195
|
+
|
|
196
|
+
| Area | Ops | Can be split across devices |
|
|
197
|
+
|------|-----|-----------------------------|
|
|
198
|
+
| Images | `blur`, `resize`, `sobel` | yes |
|
|
199
|
+
| Models | `resnet18`, `mobilenetv2` (random weights, for benchmarking) | yes |
|
|
200
|
+
| Data | `filter`, `sort`, `groupby` | `filter` only |
|
|
201
|
+
| Science | `matmul`, `fft` | yes |
|
|
202
|
+
|
|
203
|
+
## Configuration
|
|
204
|
+
|
|
205
|
+
There's nothing to configure. Two options if you need them:
|
|
206
|
+
|
|
207
|
+
- `AUTOHETERO_QUIET=1` turns off the log lines.
|
|
208
|
+
- Delete `~/.cache/autohetero/` to recalibrate. (It also recalibrates on its own when the GPU, driver or PyTorch version changes.)
|
|
209
|
+
|
|
210
|
+
## Tests
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install -e ".[dev]"
|
|
214
|
+
pytest # runs without a GPU
|
|
215
|
+
pytest -m gpu # needs an NVIDIA GPU
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Benchmarks
|
|
219
|
+
|
|
220
|
+
The evaluation scripts reproduce the results for the project's three research questions. Run them on a machine with an NVIDIA GPU:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
python bench/collect.py # benchmark dataset
|
|
224
|
+
python bench/experiments.py all # idle / GPU-stress / CPU-stress experiments
|
|
225
|
+
python eval/overhead.py # dispatch overhead
|
|
226
|
+
python eval/crossover.py # then the figures and tables, written to eval/out/
|
|
227
|
+
python eval/rq1.py
|
|
228
|
+
python eval/rq2.py
|
|
229
|
+
python eval/hybrid_gain.py
|
|
230
|
+
python eval/rq3.py
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
[REQUIREMENTS.md](https://github.com/VARITHSA/autohetero/blob/main/REQUIREMENTS.md) lists every requirement and its current status.
|
|
234
|
+
|
|
235
|
+
## Project structure
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
autohetero/ the library (dispatch, prediction, execution, built-in ops)
|
|
239
|
+
bench/ benchmark collection, experiments, load generators
|
|
240
|
+
eval/ scripts that produce the figures and tables
|
|
241
|
+
demo/ Streamlit demo
|
|
242
|
+
tests/ test suite
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Limitations
|
|
246
|
+
|
|
247
|
+
- Built for one machine with one NVIDIA GPU, for inference and data processing (not training).
|
|
248
|
+
- Hybrid mode only applies to splittable calls with at least 2 items.
|
|
249
|
+
- Very short calls aren't chunked, so they can't be rebalanced mid-call.
|
|
250
|
+
- Each call adds about 10 µs of overhead (a few ms the first time a new input size is seen).
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT — see [LICENSE](https://github.com/VARITHSA/autohetero/blob/main/LICENSE).
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# AutoHetero
|
|
2
|
+
|
|
3
|
+
  
|
|
4
|
+
|
|
5
|
+
**AutoHetero runs each PyTorch call on the CPU, the GPU, or both — whichever is fastest.**
|
|
6
|
+
|
|
7
|
+
A GPU isn't always the faster choice. For small inputs, moving data to the GPU costs more than the work itself, and a GPU that's busy with another job can end up slower than the CPU. I built AutoHetero to make that decision automatically: it benchmarks your machine once, predicts the fastest device for every call, and shifts work to the CPU when the GPU falls behind.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **One line to enable.** No configuration, no `.to("cuda")`.
|
|
12
|
+
- **Tensors or NumPy.** Results come back in the same type and on the same device you passed in.
|
|
13
|
+
- **Hybrid mode.** Large batches can be split across the CPU and GPU at the same time.
|
|
14
|
+
- **Adapts while running.** If the GPU slows down mid-call, the rest of the work is rebalanced.
|
|
15
|
+
- **Safe fallback.** GPU errors (like running out of memory) rerun the call on the CPU instead of crashing.
|
|
16
|
+
- **Transparent.** A short log line for every decision, and `autohetero.report()` shows what ran where.
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Python 3.11+ and PyTorch 2.x
|
|
21
|
+
- An NVIDIA GPU with a CUDA build of PyTorch. Without one, AutoHetero still works and runs everything on the CPU.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
**To use it in your own project**, install it from PyPI into that project's environment:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install autohetero
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**To run the demos, tests and benchmarks**, clone the repository:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/VARITHSA/autohetero.git
|
|
35
|
+
cd autohetero
|
|
36
|
+
pip install -e . # add ".[dev]" for tests, ".[demo]" for the demo
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Check that PyTorch can see your GPU (it should print `True`):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -c "import torch; print(torch.cuda.is_available())"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If it prints `False`, install the CUDA build of PyTorch for your driver from [pytorch.org](https://pytorch.org/get-started/locally/).
|
|
46
|
+
|
|
47
|
+
## Try it
|
|
48
|
+
|
|
49
|
+
From the cloned repository:
|
|
50
|
+
|
|
51
|
+
1. **With and without, side by side.** This runs the same image pipeline as plain PyTorch on the CPU, by hand on the GPU, and with AutoHetero, checks that all three give the same answer, and prints their times:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python demo/compare.py
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
2. **The demo app.** Install the demo extra, then start the app; it opens in your browser:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install -e ".[demo]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
streamlit run demo/app.py
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Pick a batch size and resolution and click **Run**. Turn on **GPU stress** to watch AutoHetero move work to the CPU. The **playground** page in the sidebar runs any built-in op at any size.
|
|
68
|
+
|
|
69
|
+
3. **The measured results.** Open `demo/showcase.ipynb` in Jupyter (`pip install jupyter` first). It reads the results from my RTX 3050 runs, so it works on a machine without a GPU.
|
|
70
|
+
|
|
71
|
+
## Using it in your code
|
|
72
|
+
|
|
73
|
+
1. Import AutoHetero and call `autohetero.enable()` once, at the start of your program.
|
|
74
|
+
2. Use the built-in ops (`autohetero.ops.*`), or put `@autohetero.auto` on your own functions.
|
|
75
|
+
3. Keep your data on the CPU, as usual. Don't call `.to("cuda")`; AutoHetero moves the data and brings the result back.
|
|
76
|
+
4. Call `autohetero.report()` at the end to see where each call ran.
|
|
77
|
+
|
|
78
|
+
**Built-in ops** — one line:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
import torch
|
|
82
|
+
import autohetero
|
|
83
|
+
|
|
84
|
+
autohetero.enable() # the one line: built-in ops now run where they're fastest
|
|
85
|
+
|
|
86
|
+
images = torch.rand(16, 3, 720, 1280) # on the CPU, as usual
|
|
87
|
+
edges = autohetero.ops.sobel(autohetero.ops.blur(images)) # each op is dispatched; the result comes back on the CPU
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Your own functions** — one decorator:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import numpy as np
|
|
94
|
+
import torch
|
|
95
|
+
import autohetero
|
|
96
|
+
|
|
97
|
+
@autohetero.auto(splittable=True)
|
|
98
|
+
def normalize(x):
|
|
99
|
+
# x arrives as a torch tensor on whichever device AutoHetero chose
|
|
100
|
+
return (x - x.mean(dim=1, keepdim=True)) / x.std(dim=1, keepdim=True)
|
|
101
|
+
|
|
102
|
+
out = normalize(np.random.rand(10_000, 512).astype(np.float32)) # numpy in -> numpy out
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Models work too. Pass them in as arguments:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
import torchvision
|
|
109
|
+
|
|
110
|
+
model = torchvision.models.resnet18(weights=None).eval()
|
|
111
|
+
|
|
112
|
+
@autohetero.auto(splittable=True)
|
|
113
|
+
def classify(images, model):
|
|
114
|
+
with torch.no_grad():
|
|
115
|
+
return model(images).argmax(1)
|
|
116
|
+
|
|
117
|
+
batch = torch.rand(64, 3, 224, 224)
|
|
118
|
+
labels = classify(batch, model)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Manual override**, when you know better:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
with autohetero.force("cpu"): # or "gpu", or "hybrid"
|
|
125
|
+
labels = classify(batch, model)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**See what happened:**
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
autohetero.report()
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
function calls modes time saved vs GPU (est.) fallbacks switches
|
|
136
|
+
-------- ----- --------------- -------- ------------------- --------- --------
|
|
137
|
+
blur 3 GPU 3 91.2 ms 12.0 ms 0 0
|
|
138
|
+
classify 2 HYBRID 1, GPU 1 612.0 ms 48.0 ms 0 1
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The first call on a new machine runs a one-time calibration (about 30 seconds).
|
|
142
|
+
|
|
143
|
+
## How it works
|
|
144
|
+
|
|
145
|
+
1. **Calibrate.** On first use, AutoHetero times its built-in ops on the CPU and GPU and caches the results in `~/.cache/autohetero/`.
|
|
146
|
+
2. **Predict.** A small learned model (random forests) estimates how long each call will take on each device. Your own functions are predicted from their own past runs; the first call measures both devices.
|
|
147
|
+
3. **Run.** Long calls run in chunks. If a chunk takes more than 1.5× longer than expected, the rest of the call is split between the CPU and GPU by their measured speeds.
|
|
148
|
+
4. **Learn.** Every call is logged with its predicted and actual time, so predictions improve over time. Call `autohetero.retrain()` to update the model.
|
|
149
|
+
|
|
150
|
+
The full design is in [DESIGN.md](https://github.com/VARITHSA/autohetero/blob/main/DESIGN.md).
|
|
151
|
+
|
|
152
|
+
## API
|
|
153
|
+
|
|
154
|
+
| Function | What it does |
|
|
155
|
+
|----------|--------------|
|
|
156
|
+
| `autohetero.enable()` | Turns on dispatch for the built-in ops and calibrates the machine (once). |
|
|
157
|
+
| `@autohetero.auto(splittable=False, batch_dim=0)` | Runs every call of a function on the fastest option. Set `splittable=True` only if each output row depends only on the matching input row; that allows chunking and Hybrid mode. |
|
|
158
|
+
| `autohetero.force(mode)` | Context manager: run on `"cpu"`, `"gpu"` or `"hybrid"`. Applies to the current thread only. |
|
|
159
|
+
| `autohetero.report()` | Prints (and returns) what ran where, total time, and the estimated time saved. |
|
|
160
|
+
| `autohetero.retrain()` | Updates the prediction model from the calls recorded so far. |
|
|
161
|
+
|
|
162
|
+
### Tips for `@auto` functions
|
|
163
|
+
|
|
164
|
+
- Use torch operations — inputs arrive as tensors, even if you passed NumPy arrays.
|
|
165
|
+
- Don't modify inputs in place (`x += 1`).
|
|
166
|
+
- Pass models as arguments, not as globals.
|
|
167
|
+
- If the GPU fails, the call reruns on the CPU, so avoid side effects you can't repeat.
|
|
168
|
+
|
|
169
|
+
## Built-in ops
|
|
170
|
+
|
|
171
|
+
| Area | Ops | Can be split across devices |
|
|
172
|
+
|------|-----|-----------------------------|
|
|
173
|
+
| Images | `blur`, `resize`, `sobel` | yes |
|
|
174
|
+
| Models | `resnet18`, `mobilenetv2` (random weights, for benchmarking) | yes |
|
|
175
|
+
| Data | `filter`, `sort`, `groupby` | `filter` only |
|
|
176
|
+
| Science | `matmul`, `fft` | yes |
|
|
177
|
+
|
|
178
|
+
## Configuration
|
|
179
|
+
|
|
180
|
+
There's nothing to configure. Two options if you need them:
|
|
181
|
+
|
|
182
|
+
- `AUTOHETERO_QUIET=1` turns off the log lines.
|
|
183
|
+
- Delete `~/.cache/autohetero/` to recalibrate. (It also recalibrates on its own when the GPU, driver or PyTorch version changes.)
|
|
184
|
+
|
|
185
|
+
## Tests
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pip install -e ".[dev]"
|
|
189
|
+
pytest # runs without a GPU
|
|
190
|
+
pytest -m gpu # needs an NVIDIA GPU
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Benchmarks
|
|
194
|
+
|
|
195
|
+
The evaluation scripts reproduce the results for the project's three research questions. Run them on a machine with an NVIDIA GPU:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
python bench/collect.py # benchmark dataset
|
|
199
|
+
python bench/experiments.py all # idle / GPU-stress / CPU-stress experiments
|
|
200
|
+
python eval/overhead.py # dispatch overhead
|
|
201
|
+
python eval/crossover.py # then the figures and tables, written to eval/out/
|
|
202
|
+
python eval/rq1.py
|
|
203
|
+
python eval/rq2.py
|
|
204
|
+
python eval/hybrid_gain.py
|
|
205
|
+
python eval/rq3.py
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
[REQUIREMENTS.md](https://github.com/VARITHSA/autohetero/blob/main/REQUIREMENTS.md) lists every requirement and its current status.
|
|
209
|
+
|
|
210
|
+
## Project structure
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
autohetero/ the library (dispatch, prediction, execution, built-in ops)
|
|
214
|
+
bench/ benchmark collection, experiments, load generators
|
|
215
|
+
eval/ scripts that produce the figures and tables
|
|
216
|
+
demo/ Streamlit demo
|
|
217
|
+
tests/ test suite
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Limitations
|
|
221
|
+
|
|
222
|
+
- Built for one machine with one NVIDIA GPU, for inference and data processing (not training).
|
|
223
|
+
- Hybrid mode only applies to splittable calls with at least 2 items.
|
|
224
|
+
- Very short calls aren't chunked, so they can't be rebalanced mid-call.
|
|
225
|
+
- Each call adds about 10 µs of overhead (a few ms the first time a new input size is seen).
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT — see [LICENSE](https://github.com/VARITHSA/autohetero/blob/main/LICENSE).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""AutoHetero: per-call CPU / GPU / Hybrid dispatch for PyTorch workloads.
|
|
2
|
+
|
|
3
|
+
import autohetero
|
|
4
|
+
autohetero.enable() # Level 1: also dispatch the built-in autohetero.ops
|
|
5
|
+
@autohetero.auto # Level 2: one function
|
|
6
|
+
with autohetero.force("cpu"): # Level 3: manual override
|
|
7
|
+
autohetero.report() # what ran where, time taken, estimated time saved
|
|
8
|
+
autohetero.retrain() # learn from this machine's measured calls
|
|
9
|
+
"""
|
|
10
|
+
from . import ops
|
|
11
|
+
from .dispatch import auto, enable, force
|
|
12
|
+
from .predictor import retrain
|
|
13
|
+
from .report import report
|
|
14
|
+
|
|
15
|
+
__all__ = ["auto", "enable", "force", "ops", "report", "retrain"]
|