|
16 | 16 | from io import StringIO |
17 | 17 | import pickle |
18 | 18 |
|
19 | | -from .utils.io import zipopen |
| 19 | +from pypop.utils.io import zipopen |
20 | 20 |
|
21 | 21 | try: |
22 | 22 | from tqdm.auto import tqdm |
@@ -85,20 +85,49 @@ class PRV: |
85 | 85 |
|
86 | 86 | def __init__(self, prv_path): |
87 | 87 |
|
88 | | - try: |
89 | | - self._load_prv_and_pcf(prv_path) |
| 88 | + self._prv_path = prv_path |
| 89 | + self._no_prv = False |
90 | 90 |
|
91 | | - except ValueError: |
| 91 | + self._omp_region_data = None |
| 92 | + self._event_names = {} |
| 93 | + self._event_vals = {} |
| 94 | + self.state = None |
| 95 | + self.event = None |
| 96 | + self.comm = None |
| 97 | + |
| 98 | + try: |
| 99 | + self._load_pickle(prv_path) |
| 100 | + self._no_prv = True |
| 101 | + except (pickle.UnpicklingError, ValueError): |
92 | 102 | try: |
93 | | - self._load_pickle(prv_path) |
94 | | - except ValueError: |
95 | | - raise ValueError("Not a prv or valid pickle") |
| 103 | + self._load_pickle(PRV._generate_event_cache_name(prv_path)) |
| 104 | + except (pickle.UnpicklingError, ValueError): |
| 105 | + try: |
| 106 | + self._load_prv_and_pcf(prv_path) |
| 107 | + self.save(PRV._generate_event_cache_name(prv_path)) |
| 108 | + except ValueError: |
| 109 | + raise ValueError("Not a prv or valid pickle") |
96 | 110 |
|
97 | | - self._omp_region_data = None |
| 111 | + @staticmethod |
| 112 | + def _generate_event_cache_name(prvfile): |
| 113 | + return prvfile + ".eventcache" |
| 114 | + |
| 115 | + @staticmethod |
| 116 | + def _generate_region_cache_name(prvfile): |
| 117 | + if "eventcache" in prvfile: |
| 118 | + return prvfile.replace("eventcache", "regioncache") |
| 119 | + |
| 120 | + return prvfile + ".regioncache" |
| 121 | + |
| 122 | + def reload(self): |
| 123 | + if self._no_prv: |
| 124 | + warn("Data loaded directly from cachefile, reload from PRV not possible") |
| 125 | + return |
| 126 | + |
| 127 | + self._parse_pcf() |
| 128 | + self._parse_prv() |
98 | 129 |
|
99 | 130 | def _load_prv_and_pcf(self, prv_path): |
100 | | - self._event_names = {} |
101 | | - self._event_vals = {} |
102 | 131 | self._parse_pcf(prv_path) |
103 | 132 |
|
104 | 133 | self._parse_prv(prv_path) |
@@ -196,33 +225,49 @@ def _process_commline(self, line, writer): |
196 | 225 | pass |
197 | 226 |
|
198 | 227 | def save(self, filename): |
199 | | - savedata = (self.metadata, self.state, self.event, self.comm) |
| 228 | + savedata = ( |
| 229 | + self.metadata, |
| 230 | + self.state, |
| 231 | + self.event, |
| 232 | + self.comm, |
| 233 | + self._event_names, |
| 234 | + self._event_vals, |
| 235 | + ) |
200 | 236 |
|
201 | 237 | with gzip.open(filename, "wb", compresslevel=6) as fh: |
202 | 238 | pickle.dump(savedata, fh) |
203 | 239 |
|
204 | 240 | def _load_pickle(self, filename): |
205 | | - try: |
206 | | - with gzip.open(filename, "rb") as fh: |
207 | | - data = pickle.load(fh) |
208 | | - except gzip.BadGzipFile: |
209 | | - try: |
210 | | - with open(filename, "rb") as fh: |
211 | | - data = pickle.load(fh) |
212 | | - except pickle.UnpicklingError: |
213 | | - raise ValueError("Invalid pickle -- missing data") |
| 241 | + with zipopen(filename, "rb") as fh: |
| 242 | + data = pickle.load(fh) |
214 | 243 |
|
215 | | - self.metadata, self.state, self.event, self.comm = data |
| 244 | + try: |
| 245 | + ( |
| 246 | + self.metadata, |
| 247 | + self.state, |
| 248 | + self.event, |
| 249 | + self.comm, |
| 250 | + self._event_names, |
| 251 | + self._event_vals, |
| 252 | + ) = data |
216 | 253 | except ValueError: |
217 | 254 | raise ValueError("Invalid pickle -- missing data") |
218 | 255 |
|
219 | | - def profile_openmp_regions(self, no_progress=False): |
| 256 | + def profile_openmp_regions(self, no_progress=False, ignore_cache=False): |
220 | 257 | """Profile OpenMP Region Info |
221 | 258 | """ |
222 | 259 |
|
223 | 260 | if self._omp_region_data is not None: |
224 | 261 | return self._omp_region_data |
225 | 262 |
|
| 263 | + if not ignore_cache: |
| 264 | + try: |
| 265 | + with zipopen(PRV._generate_region_cache_name(self._prv_path), 'rb') as fh: |
| 266 | + self._omp_region_data = pickle.load(fh) |
| 267 | + return self._omp_region_data |
| 268 | + except (FileNotFoundError, pickle.UnpicklingError): |
| 269 | + pass |
| 270 | + |
226 | 271 | idx_master_threads = pd.IndexSlice[:, 1] |
227 | 272 |
|
228 | 273 | # First generate appropriate event subsets grouped by rank |
@@ -419,6 +464,9 @@ def profile_openmp_regions(self, no_progress=False): |
419 | 464 |
|
420 | 465 | self._omp_region_data = pd.concat(rank_stats, names=["rank", "region"]) |
421 | 466 |
|
| 467 | + with zipopen(PRV._generate_region_cache_name(self._prv_path), 'wb') as fh: |
| 468 | + pickle.dump(self._omp_region_data, fh) |
| 469 | + |
422 | 470 | return self._omp_region_data |
423 | 471 |
|
424 | 472 | def region_location_from_fingerprint(self, fingerprint): |
@@ -470,41 +518,42 @@ def openmp_region_summary(self, by_location=False): |
470 | 518 | fingerprint_key = "Region Function Fingerprint" |
471 | 519 |
|
472 | 520 | runtime = self.metadata.ns_elapsed |
| 521 | + nproc = len(set(self._omp_region_data["Rank"])) |
473 | 522 |
|
474 | 523 | self.profile_openmp_regions() |
475 | 524 |
|
476 | 525 | summary = self._omp_region_data.groupby(fingerprint_key).agg( |
477 | 526 | **{ |
478 | 527 | "Instances": ("Maximum Computation Time", "count"), |
479 | | - "Relative Load Balance Efficiency": ( |
480 | | - "Load Balance", |
481 | | - lambda x: np.average( |
482 | | - x, weights=self._omp_region_data.loc[x.index, "Region Length"], |
483 | | - ), |
| 528 | + "Total Parallel Inefficiency Contribution": ( |
| 529 | + "Region Delay Time", |
| 530 | + lambda x: np.sum(x) / (nproc * runtime), |
484 | 531 | ), |
485 | | - "Relative Parallel Efficiency": ( |
| 532 | + "Total Load Imbalance Contribution": ( |
| 533 | + "Computation Delay Time", |
| 534 | + lambda x: np.sum(x) / (nproc * runtime), |
| 535 | + ), |
| 536 | + "Average Parallel Efficiency": ( |
486 | 537 | "Parallel Efficiency", |
487 | 538 | lambda x: np.average( |
488 | 539 | x, weights=self._omp_region_data.loc[x.index, "Region Length"] |
489 | 540 | ), |
490 | 541 | ), |
491 | | - "Load Balance Efficiency": ( |
492 | | - "Computation Delay Time", |
493 | | - lambda x: 1 - np.sum(x / runtime), |
494 | | - ), |
495 | | - "Parallel Efficiency": ( |
496 | | - "Region Delay Time", |
497 | | - lambda x: 1 - np.sum(x / runtime), |
| 542 | + "Average Load Balance": ( |
| 543 | + "Load Balance", |
| 544 | + lambda x: np.average( |
| 545 | + x, weights=self._omp_region_data.loc[x.index, "Region Length"], |
| 546 | + ), |
498 | 547 | ), |
499 | 548 | "Accumulated Region Time": ("Region Length", np.sum), |
500 | 549 | "Accumulated Computation Time": ("Region Total Computation", np.sum), |
501 | 550 | "Average Computation Time": ("Average Computation Time", np.average), |
502 | 551 | "Maximum Computation Time": ("Maximum Computation Time", np.max), |
503 | | - "Region Functions": (fingerprint_key, lambda x: x[0],), |
| 552 | + "Region Functions": (fingerprint_key, lambda x: x.iloc[0],), |
504 | 553 | } |
505 | 554 | ) |
506 | 555 |
|
507 | | - return summary.sort_values("Load Balance Efficiency") |
| 556 | + return summary.sort_values("Total Parallel Inefficiency Contribution") |
508 | 557 |
|
509 | 558 |
|
510 | 559 | def _format_timedate(prv_td): |
|
0 commit comments