femtocrux 1.2.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,109 +0,0 @@
1
- import numpy as np
2
- from .sim_io import SimIOWrapper
3
- import os
4
- from femtobehav.fasmir import FASMIR
5
- from femtobehav.sim import SimRunner
6
- import tempfile
7
- from typing import Tuple, Any
8
- import zipfile
9
-
10
-
11
- class CompilerFrontend:
12
- """A generic compiler frontend, must be subclassed for each input IR/framework"""
13
-
14
- def __init__(
15
- self, input_ir: Any, fasmir: FASMIR = None, io_wrapper: SimIOWrapper = None
16
- ):
17
- self.input_ir = input_ir
18
- self.fasmir = fasmir
19
- self.io_wrapper = io_wrapper
20
-
21
- @property
22
- def is_compiled(self):
23
- return self.fasmir is not None and self.io_wrapper is not None
24
-
25
- def _compile(self, input_ir: Any, options: dict) -> Tuple[FASMIR, SimIOWrapper]:
26
- """
27
- Runs FM compiler to generate FASMIR, and encode io information in a
28
- SimIOWrapper object.
29
-
30
- Must be implemented for each frontend subclass.
31
-
32
- Must return a tuple pair (FASMIR, SimIOWrapper)
33
- """
34
- raise NotImplementedError(
35
- "Subclasses need to implement this based on their input ir"
36
- )
37
-
38
- def compile(self, options: dict = {}):
39
- if not self.is_compiled:
40
- self.fasmir, self.io_wrapper = self._compile(self.input_ir, options)
41
-
42
- def dump_bitfile(self, encrypt: bool = True) -> bytes:
43
- """Dumps a bitfile used to program the SPU."""
44
- if not self.is_compiled:
45
- raise RuntimeError("Model must be compiled before dumping bitfile")
46
-
47
- with tempfile.TemporaryFile() as tmpfile:
48
- with tempfile.TemporaryDirectory() as dirname:
49
- # Dump memory files to a directory
50
- runner = SimRunner(self.fasmir, data_dir=dirname, encrypt=encrypt)
51
- runner.reset()
52
- runner.finish()
53
-
54
- # Archive the directory
55
- with zipfile.ZipFile(
56
- tmpfile, mode="w", compression=zipfile.ZIP_DEFLATED
57
- ) as archive:
58
- for relpath in os.listdir(dirname):
59
- abspath = os.path.join(dirname, relpath)
60
- archive.write(abspath, arcname=relpath)
61
-
62
- # Read out the bytes in the archive
63
- tmpfile.seek(0)
64
- bitfile = tmpfile.read()
65
-
66
- return bitfile
67
-
68
- def _get_padded_len(self, fasmir: FASMIR, name: str):
69
- try:
70
- fasmir_var = fasmir.data_vars[name]
71
- except KeyError:
72
- raise ValueError(
73
- "Failed to find FASMIR variable corresponding to name %s" % name
74
- )
75
- return fasmir_var.numpy.shape[0]
76
-
77
- def run_behavioral_simulator(
78
- self,
79
- *args: np.ndarray,
80
- input_period: float = None,
81
- quantize_inputs=True,
82
- dequantize_outputs=True,
83
- **kwargs
84
- ):
85
- """
86
- Runs the behavioral simulator and returns outputs and metrics.
87
-
88
- Arguments:
89
- args (np.ndarray): Input tensors to the simulator, as numpy arrays. Either
90
- floating-point or integer (see `quantize_inputs` for
91
- more detail on input datatypes).
92
- input_period (float, optional): total simulation time.
93
- quantize_inputs (bool, optional): If True, then floating-point inputs will
94
- be quantized to integer before passing into the simulator.
95
- Otherwise, the simulator expects that the inputs will
96
- already be in integer format. Default True.
97
- dequantize_outputs (bool: optional): If True, the integer outputs from the
98
- simulator will be cast back to the original
99
- floating-point domain. Otherwise, the outputs will be
100
- returned as integers. Default True.
101
-
102
- """
103
- return self.io_wrapper.run(
104
- *args,
105
- input_period=input_period,
106
- quantize_inputs=quantize_inputs,
107
- dequantize_outputs=dequantize_outputs,
108
- **kwargs
109
- )
@@ -1,184 +0,0 @@
1
- import numpy as np
2
- import tabulate
3
- from collections import defaultdict
4
- from typing import List, Union, Dict
5
-
6
- PREFIXES = {
7
- 1e-15: "f",
8
- 1e-12: "p",
9
- 1e-9: "n",
10
- 1e-6: "µ",
11
- 1e-3: "m",
12
- 1: "",
13
- 1e3: "k",
14
- 1e6: "M",
15
- 1e9: "G",
16
- }
17
-
18
-
19
- def to_sci(x):
20
- """Convert a float to a scientific-prefix formatted string
21
-
22
- e.g. 0.004531 -> '4.531 m'
23
- """
24
- ks = np.array(list(PREFIXES.keys()))
25
- ks_cut = ks[ks <= np.abs(x)]
26
- if len(ks_cut) > 0:
27
- k = np.max(ks[ks <= x])
28
- pref = PREFIXES[k]
29
- x = x / k
30
- return f"{x:.3g} {pref}"
31
- else:
32
- return f"{x:.3g} "
33
-
34
-
35
- def _merge_dicts(dicts: List[dict]):
36
- """Merges list of dicts into dict of lists"""
37
- output = defaultdict(list)
38
- for d in dicts:
39
- for k, v in d.items():
40
- output[k].append(v)
41
- for k, v in output.items():
42
- if isinstance(v[0], dict):
43
- output[k] = _merge_dicts(v)
44
- return dict(output)
45
-
46
-
47
- ALLOWED_KEYS = [
48
- "Dynamic Energy/Frame (J)",
49
- "Total Energy/Frame (J)",
50
- "Latency (s)",
51
- "Static Energy/Frame (J)",
52
- "Memory",
53
- "Frames Simulated",
54
- "Power (W)",
55
- ]
56
-
57
-
58
- class SimMetrics:
59
- """
60
- Object storing hardware simulator metrics.
61
-
62
- Arguments:
63
- metrics (list[dict] or dict[list]): measured metrics per batch
64
- from simulator
65
- dt (float, optional): total duration of the simulation, in seconds.
66
- If not provided, will use the total active time of the sim, but this will
67
- overlook the time spent sleeping.
68
-
69
- Attributes:
70
- total_energy: average total energy in Joules
71
- total_active_time: average time spent processing in seconds
72
- latency_per_frame: active time divided by the number of processed
73
- input frames
74
- power: average power consumption, in Watts
75
-
76
- metrics: detailed metrics dictionary
77
-
78
- """
79
-
80
- def __init__(
81
- self,
82
- metrics: Union[List[Dict[str, float]], Dict[str, List[float]]],
83
- dt=None,
84
- reduction_mode: str = "mean",
85
- ):
86
- if isinstance(metrics, list):
87
- metrics = _merge_dicts(metrics)
88
-
89
- for k in list(metrics.keys()):
90
- if k not in ALLOWED_KEYS:
91
- metrics.pop(k)
92
-
93
- self.metrics = metrics
94
- self.dt = dt
95
-
96
- self.reduction_mode = reduction_mode
97
- if reduction_mode == "mean":
98
- self.reduce = np.mean
99
- elif reduction_mode == "sum":
100
- self.reduce = np.sum
101
-
102
- @property
103
- def num_frames(self):
104
- return self.reduce(self.metrics["Frames Simulated"])
105
-
106
- @property
107
- def total_energy(self):
108
- return self.reduce(self.metrics["Total Energy/Frame (J)"]) * self.num_frames
109
-
110
- @property
111
- def total_dynamic_energy(self):
112
- return self.reduce(self.metrics["Dynamic Energy/Frame (J)"]) * self.num_frames
113
-
114
- @property
115
- def total_static_energy(self):
116
- return self.reduce(self.metrics["Static Energy/Frame (J)"]) * self.num_frames
117
-
118
- @property
119
- def total_active_time(self):
120
- return self.reduce(self.metrics["Latency (s)"]) * self.num_frames
121
-
122
- @property
123
- def latency(self):
124
- return self.reduce(self.metrics["Latency (s)"])
125
-
126
- @property
127
- def total_time(self):
128
- if self.dt is not None:
129
- dt = self.dt * self.metrics["Frames Simulated"][0]
130
- return max(dt, self.total_active_time)
131
- else:
132
- return self.total_active_time
133
-
134
- @property
135
- def power(self):
136
- return self.reduce(self.metrics["Power (W)"])
137
-
138
- def performance_report(self):
139
- report = [
140
- ["total energy", f"{to_sci(self.total_energy)}J"],
141
- ["total dynamic energy", f"{to_sci(self.total_dynamic_energy)}J"],
142
- ["total static energy", f"{to_sci(self.total_static_energy)}J"],
143
- ["power", f"{to_sci(self.power)}W"],
144
- ["total active time", f"{to_sci(self.total_active_time)}s"],
145
- ["total time", f"{to_sci(self.total_time)}s"],
146
- ["latency/frame", f"{to_sci(self.latency)}s"],
147
- ]
148
- output = tabulate.tabulate(report)
149
- return output
150
-
151
- def memory_report(self):
152
- mem = self.metrics["Memory"]
153
-
154
- out = {}
155
- for mem_type in ["Data Mem", "Instr Mem", "Table Mem"]:
156
- k = f"{mem_type} (B)"
157
- out[mem_type] = {
158
- "Used": int(mem["Used"][k][0]),
159
- "Capacity": int(mem["Capacity"][k][0]),
160
- }
161
-
162
- full_names = {
163
- "Data Mem": "Data Memory",
164
- "Instr Mem": "Instruction Memory",
165
- "Table Mem": "Table Memory",
166
- }
167
-
168
- report = [["Memory Type", "Used", "Capacity", "Percentage"]]
169
- for key, name in full_names.items():
170
- used = out[key]["Used"]
171
- cap = out[key]["Capacity"]
172
- pct = 100 * used / cap
173
- report.append([name, f"{to_sci(used)}B", f"{to_sci(cap)}B", f"{pct:.1f}%"])
174
- return tabulate.tabulate(report, headers="firstrow")
175
-
176
- def __repr__(self):
177
- perf = self.performance_report()
178
- mem = self.memory_report()
179
- output = f"Behavioral Simulator Metrics, {self.reduction_mode} over batches"
180
- output += f"\n\n{perf}"
181
- output += f"\n\n{mem}"
182
-
183
- output = ("-" * 60 + "\n") + output + ("\n" + "-" * 60)
184
- return output