Skip to content

Commit 5d04ea0

Browse files
committed
Rewritten to use numpy for faster parsing
1 parent 25b48b8 commit 5d04ea0

File tree

10 files changed

+317
-292
lines changed

10 files changed

+317
-292
lines changed

data/miniFE/profile.cubex

18.3 MB
Binary file not shown.

pycubexr/classes/metric_values.py

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import logging
12
import warnings
23
from typing import List, Any
34

5+
import numpy as np
6+
47
from pycubexr.classes import CNode, Metric
58
from pycubexr.classes.metric import MetricType
6-
from pycubexr.classes.values import BaseValue
9+
from pycubexr.classes.values import CubeValues
710
from pycubexr.utils.exceptions import InvalidConversionInstructionError
811

912

@@ -14,7 +17,7 @@ def __init__(
1417
*,
1518
metric: Metric,
1619
cnode_indices: List[int],
17-
values: List[Any]
20+
values: np.ndarray
1821
):
1922
self.metric = metric
2023
self.values = values
@@ -38,11 +41,11 @@ def cnode_values(self, cnode: CNode, convert_to_exclusive: bool = False, convert
3841

3942
cid = self.metric.tree_enumeration[cnode.id]
4043
if cid not in self.cnode_indices:
41-
values = [0] * self.num_locations()
44+
values = np.zeros(self.num_locations(), dtype=self.values.dtype)
4245
else:
4346
start_index = int(self.cnode_indices[cid] * self.num_locations())
4447
end_index = start_index + self.num_locations()
45-
values = self.values[start_index: end_index] # creates copy
48+
values = self.values[start_index: end_index] # creates no copy
4649

4750
must_convert = ((convert_to_exclusive and self.metric.metric_type == MetricType.INCLUSIVE)
4851
or (convert_to_inclusive and self.metric.metric_type == MetricType.EXCLUSIVE))
@@ -70,32 +73,65 @@ def _isub(a, b):
7073
for i, y in enumerate(b):
7174
a[i] -= y
7275

73-
def _convert_values(self, cnode: CNode, values: List[Any], to_inclusive: bool = True):
76+
@staticmethod
77+
def _detect_negative_overflow(a, b):
78+
if np.issubdtype(a.dtype, np.unsignedinteger):
79+
check = a < b
80+
if np.any(check):
81+
b = b.copy()
82+
b[check] = a[check]
83+
elif np.issubdtype(a.dtype, np.signedinteger):
84+
pre_check = a > 0
85+
check = np.full_like(pre_check, False, dtype=bool)
86+
check[~pre_check] = -b[~pre_check] < np.iinfo(a.dtype).min - a[~pre_check]
87+
check[pre_check] |= -b[pre_check] > np.iinfo(a.dtype).max - a[pre_check]
88+
if np.any(check):
89+
if not np.issubdtype(a.dtype, np.int64):
90+
bigger_type = np.min_scalar_type(int(np.iinfo(a.dtype).min) - 1)
91+
b = b.astype(bigger_type, copy=False)
92+
else:
93+
b = b.copy()
94+
b[~pre_check & check] = -(np.iinfo(a.dtype).min - a[~pre_check & check])
95+
b[pre_check & check] = -(np.iinfo(a.dtype).max - a[pre_check & check])
96+
return b
97+
98+
def _convert_values(self, cnode: CNode, values: np.ndarray, to_inclusive: bool = True):
7499
# Go over all cnode children and add the metric values
75-
# Does change the values array!
100+
values = values.copy()
76101
for child_cnode in cnode.get_children():
77102
child_values = self.cnode_values(child_cnode,
78103
convert_to_inclusive=True,
79104
convert_to_exclusive=False)
80105
if to_inclusive:
81-
self._iadd(values, child_values)
106+
values += child_values
82107
else:
83-
self._isub(values, child_values)
108+
if isinstance(values, np.ndarray) and np.issubdtype(values.dtype, np.integer):
109+
child_values = self._detect_negative_overflow(values, child_values)
110+
values -= child_values
84111
return values
85112

86113
def value(self, cnode: CNode, convert_to_inclusive=False, convert_to_exclusive=False):
87-
res = sum(self.cnode_values(cnode, convert_to_inclusive, convert_to_exclusive))
88-
if isinstance(res, BaseValue):
89-
return res.try_convert()
114+
res = self.cnode_values(cnode, convert_to_inclusive, convert_to_exclusive)
115+
sum_ = res.sum()
116+
if isinstance(res, np.ndarray) and np.issubdtype(res.dtype, np.integer):
117+
if res.max() > np.iinfo(res.dtype).max // len(res):
118+
logging.info("Used overflow prevention for {0} of {1}".format(self.metric.name, cnode.region.name))
119+
# Overflow prevention, really slow
120+
sum_ = 0
121+
for r in res:
122+
sum_ += int(r)
123+
124+
if isinstance(sum_, CubeValues):
125+
return sum_.astype(float)
90126
else:
91-
return res
127+
return sum_
92128

93129
def mean(self, cnode: CNode, convert_to_inclusive=False, convert_to_exclusive=False):
94-
values = self.cnode_values(cnode, convert_to_inclusive, convert_to_exclusive)
95-
res = sum(values)
96-
if isinstance(res, BaseValue):
97-
res = res.try_convert()
98-
return res / len(values)
130+
res = self.cnode_values(cnode, convert_to_inclusive, convert_to_exclusive).mean()
131+
if isinstance(res, CubeValues):
132+
return res.astype(float)
133+
else:
134+
return res
99135

100136
def __repr__(self):
101137
return 'MetricValues<{}>'.format(self.__dict__)

0 commit comments

Comments
 (0)