-
Notifications
You must be signed in to change notification settings - Fork 588
/
timing_measurements.py
193 lines (174 loc) · 7.55 KB
/
timing_measurements.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
from typing import Dict, List, Optional
import numpy as np
import meep as mp
# Codes for different Meep time sinks, used by `mp.Simulation.time_spent_on()`.
# See
# https://meep.readthedocs.io/en/latest/Python_User_Interface/#simulation-time
# for more information.
TIMING_MEASUREMENT_IDS = {
"connecting_chunks": mp.Connecting,
"time_stepping": mp.Stepping,
"boundaries_copying": mp.Boundaries,
"mpi_all_to_all": mp.MpiAllTime,
"mpi_one_to_one": mp.MpiOneTime,
"field_output": mp.FieldOutput,
"fourier_transform": mp.FourierTransforming,
"mpb": mp.MPBTime,
"near_to_farfield_transform": mp.GetFarfieldsTime,
"other": mp.Other,
"field_update_b": mp.FieldUpdateB,
"field_update_h": mp.FieldUpdateH,
"field_update_d": mp.FieldUpdateD,
"field_update_e": mp.FieldUpdateE,
"boundary_stepping_b": mp.BoundarySteppingB,
"boundary_stepping_wh": mp.BoundarySteppingWH,
"boundary_stepping_ph": mp.BoundarySteppingPH,
"boundary_stepping_h": mp.BoundarySteppingH,
"boundary_stepping_d": mp.BoundarySteppingD,
"boundary_stepping_we": mp.BoundarySteppingWE,
"boundary_stepping_pe": mp.BoundarySteppingPE,
"boundary_stepping_e": mp.BoundarySteppingE,
}
def get_current_time_step(sim: mp.Simulation) -> int:
"""Gets the current time step from a Meep simulation object."""
return sim.fields.t
class MeepTimingMeasurements:
"""Container for Meep timing measurements and performance information.
See
https://meep.readthedocs.io/en/latest/Python_User_Interface/#simulation-time
for more information on the Meep timing measurements.
Attributes:
measurements: a dictionary of timing measurements, where each entry is a
list containing timing measurements for each process.
elapsed_time: the simulation's elapsed wall time.
num_time_steps: the total number of time steps taken in the simulation.
time_per_step: the wall time taken per step. Each element of this list is
expected to correspond to a unique time step, but could also be averaged
over several time steps.
dft_relative_change: the relative change in the DFT fields, measured at each
time step.
overlap_relative_change: the relative change in the mode overlaps, measured
at each time step.
relative_energy: the relative energy in the simulation domain, measured
at each time step.
measurement_names: a list of measurement names stored in `measurements`.
comm_efficiency: the communication efficiency, defined as the mean
MPI/synchronization time divided by the sum of the mean time taken for
time stepping and the mean time taken for fourier transforming. This is
zero if no simulation work has been performed.
comm_efficiency_one_to_one: MPI one-to-one communication efficiency, defined
as the mean MPI one-to-one communication time divided by the sum of the
mean time taken for time stepping and the mean time taken for fourier
transforming. This is zero if no simulation work has been performed.
comm_efficiency_all_to_all: MPI all-to-all communication efficiency, defined
as the mean MPI all-to-all communication time divided by the sum of the
mean time taken for time stepping and the mean time taken for fourier
transforming. This is zero if no simulation work has been performed.
"""
def __init__(
self,
measurements: Dict[str, List[float]],
elapsed_time: float,
num_time_steps: int,
time_per_step: List[float],
dft_relative_change: List[float],
overlap_relative_change: List[float],
relative_energy: List[float],
):
self.measurements = measurements
self.elapsed_time = elapsed_time
self.num_time_steps = num_time_steps
self.time_per_step = time_per_step
self.dft_relative_change = dft_relative_change
self.overlap_relative_change = overlap_relative_change
self.relative_energy = relative_energy
@classmethod
def new_from_simulation(
cls,
sim: mp.Simulation,
elapsed_time: Optional[float] = -1,
time_per_step: Optional[List[float]] = None,
dft_relative_change: Optional[List[float]] = None,
overlap_relative_change: Optional[List[float]] = None,
relative_energy: Optional[List[float]] = None,
) -> "MeepTimingMeasurements":
"""Creates a new `MeepTimingMeasurements` from a Meep simulation object.
Usage example:
```
sim = mp.Simulation(...)
sim.init_sim()
start_time = time.time()
sim.run(...)
measurements = meep_timing.MeepTimingMeasurements.new_from_simulation(
sim=sim,
elapsed_time=time.time() - start_time,
)
```
Args:
sim: a Meep simulation object that has previously been run.
elapsed_time: the wall time that has elapsed while running the simulation.
time_per_step: the wall time taken per step. Each element of this list is
expected to correspond to a unique time step, but could also be averaged
over several time steps.
dft_relative_change: the relative change in the DFT fields, measured at
each time step.
overlap_relative_change: the relative change in the mode overlaps,
measured at each time step.
relative_energy: the relative energy in the simulation domain, measured
at each time step.
Returns:
the resulting `MeepTimingMeasurements`
"""
measurements = {
name: sim.time_spent_on(timing_id).tolist()
for name, timing_id in TIMING_MEASUREMENT_IDS.items()
}
return cls(
measurements=measurements,
elapsed_time=elapsed_time,
num_time_steps=get_current_time_step(sim),
time_per_step=time_per_step if time_per_step is not None else [],
dft_relative_change=dft_relative_change
if dft_relative_change is not None
else [],
overlap_relative_change=overlap_relative_change
if overlap_relative_change is not None
else [],
relative_energy=relative_energy if relative_energy is not None else [],
)
@property
def measurement_names(self) -> List[str]:
return list(self.measurements)
@property
def comm_efficiency(self) -> float:
computation_time = np.mean(self.measurements["time_stepping"]) + np.mean(
self.measurements["fourier_transform"]
)
if computation_time == 0:
return 0
else:
return (
np.mean(
self.measurements["mpi_all_to_all"]
+ self.measurements["mpi_one_to_one"]
)
/ computation_time
)
@property
def comm_efficiency_one_to_one(self) -> float:
computation_time = np.mean(self.measurements["time_stepping"]) + np.mean(
self.measurements["fourier_transform"]
)
if computation_time == 0:
return 0
else:
return np.mean(self.measurements["mpi_one_to_one"]) / computation_time
@property
def comm_efficiency_all_to_all(self) -> float:
computation_time = np.mean(self.measurements["time_stepping"]) + np.mean(
self.measurements["fourier_transform"]
)
if computation_time == 0:
return 0
else:
return np.mean(self.measurements["mpi_all_to_all"]) / computation_time