mirror of
https://github.com/jlengrand/bugsink.git
synced 2026-03-10 08:01:17 +00:00
prev_tup with n parameter
this makes the usages of prev_tup approx 3 times faster
This commit is contained in:
@@ -25,30 +25,38 @@ TL_HOUR = 4
|
||||
TL_MINUTE = 5
|
||||
|
||||
|
||||
def apply_n(f, n, v):
|
||||
for i in range(n):
|
||||
v = f(v)
|
||||
return v
|
||||
|
||||
|
||||
def _prev_tup(tup):
|
||||
def _prev_tup(tup, n=1):
|
||||
aslist = list(tup)
|
||||
|
||||
# if n > 1 we try to first remove the largest possible chunk from the last element of the tuple, so that we can
|
||||
# then do the remainder in the loop (for performance reasons)
|
||||
if n > 1:
|
||||
DONE_IN_LOOP = 1
|
||||
first_chunk = min(n - DONE_IN_LOOP, max(0, tup[-1] - MIN_VALUE_AT_TUP_INDEX[-1] - DONE_IN_LOOP))
|
||||
aslist[-1] -= first_chunk
|
||||
remainder = n - first_chunk - DONE_IN_LOOP
|
||||
else:
|
||||
remainder = 0
|
||||
|
||||
for tup_index, val in reversed(list(enumerate(aslist))):
|
||||
if aslist[tup_index] == MIN_VALUE_AT_TUP_INDEX[tup_index]:
|
||||
if tup_index == 2:
|
||||
# day roll-over: just use a datetime
|
||||
aslist = list((datetime(*aslist, tzinfo=timezone.utc) - timedelta(days=1)).timetuple()[:len(tup)])
|
||||
break
|
||||
break # we've used a timedelta, so we don't need to do months/years "by hand" in the loop
|
||||
|
||||
else:
|
||||
# roll over to max
|
||||
aslist[tup_index] = MAX_VALUE_AT_TUP_INDEX[tup_index]
|
||||
# implied because no break: continue with the left hand side
|
||||
# implied because no break: continue with the left hand side of the tuple
|
||||
|
||||
else:
|
||||
aslist[tup_index] -= 1
|
||||
break
|
||||
|
||||
if remainder > 0:
|
||||
return _prev_tup(aslist, remainder)
|
||||
|
||||
return tuple(aslist)
|
||||
|
||||
|
||||
@@ -58,7 +66,7 @@ def _inc(d, tup, n, max_age):
|
||||
if tup not in d:
|
||||
if len(d) > 0:
|
||||
new_period = True
|
||||
min_tup = apply_n(_prev_tup, max_age - 1, tup)
|
||||
min_tup = _prev_tup(tup, max_age - 1)
|
||||
for k, v in list(d.items()):
|
||||
if k < min_tup:
|
||||
del d[k]
|
||||
@@ -123,7 +131,7 @@ class PeriodCounter(object):
|
||||
}[period_name]
|
||||
|
||||
def _get_event_state(self, tup, tl, how_many_periods, gte_threshold):
|
||||
min_tup = apply_n(_prev_tup, how_many_periods - 1, tup)
|
||||
min_tup = _prev_tup(tup, how_many_periods - 1) if tup != () else ()
|
||||
d = self.counts[tl]
|
||||
total = sum([v for k, v in d.items() if k >= min_tup])
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class callback(object):
|
||||
|
||||
class PeriodCounterTestCase(TestCase):
|
||||
|
||||
def test_prev_tup(self):
|
||||
def test_prev_tup_near_rollover(self):
|
||||
self.assertEquals((2020,), _prev_tup((2021,)))
|
||||
|
||||
self.assertEquals((2020, 1), _prev_tup((2020, 2)))
|
||||
@@ -40,12 +40,20 @@ class PeriodCounterTestCase(TestCase):
|
||||
self.assertEquals((2020, 1, 1, 0, 0), _prev_tup((2020, 1, 1, 0, 1)))
|
||||
self.assertEquals((2019, 12, 31, 23, 59), _prev_tup((2020, 1, 1, 0, 0)))
|
||||
|
||||
def test_prev_tup_large_number_of_applications(self):
|
||||
self.assertEquals((1920,), apply_n(_prev_tup, 100, (2020,)))
|
||||
self.assertEquals((2010, 5), apply_n(_prev_tup, 120, (2020, 5)))
|
||||
self.assertEquals((2019, 5, 7,), apply_n(_prev_tup, 366, (2020, 5, 7)))
|
||||
self.assertEquals((2020, 5, 6, 20,), apply_n(_prev_tup, 24, (2020, 5, 7, 20,)))
|
||||
self.assertEquals((2020, 5, 6, 20, 12), apply_n(_prev_tup, 1440, (2020, 5, 7, 20, 12)))
|
||||
|
||||
def test_prev_tup_with_explicit_n(self):
|
||||
self.assertEquals(_prev_tup((2020,), 100), apply_n(_prev_tup, 100, (2020,)))
|
||||
self.assertEquals(_prev_tup((2020, 5), 120), apply_n(_prev_tup, 120, (2020, 5)))
|
||||
self.assertEquals(_prev_tup((2020, 5, 7), 366), apply_n(_prev_tup, 366, (2020, 5, 7)))
|
||||
self.assertEquals(_prev_tup((2020, 5, 7, 20,), 24), apply_n(_prev_tup, 24, (2020, 5, 7, 20,)))
|
||||
self.assertEquals(_prev_tup((2020, 5, 7, 20, 12), 1440), apply_n(_prev_tup, 1440, (2020, 5, 7, 20, 12)))
|
||||
|
||||
def test_foo(self):
|
||||
datetime_utc = datetime.now(timezone.utc) # basically I just want to write this down somewhere
|
||||
pc = PeriodCounter()
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
## _prev_tup()
|
||||
|
||||
1_000 iterations of _prev_tup in 0.794ms. The main thing we care about is not this little
|
||||
1_000 iterations of _prev_tup in 0.816ms. The main thing we care about is not this little
|
||||
private helper though, but PeriodCounter.inc(). Let's test that next.
|
||||
|
||||
|
||||
## PeriodCounter.inc()
|
||||
|
||||
1_000 iterations of PeriodCounter.inc() in 25.186ms. We care about evaluation of some event more though. Let's
|
||||
1_000 iterations of PeriodCounter.inc() in 7.766ms. We care about evaluation of some event more though. Let's
|
||||
test that next.
|
||||
|
||||
## PeriodCounter.inc()
|
||||
|
||||
1_000 iterations of PeriodCounter.inc() in 93.481ms. (when 3 event-listeners are active). I'm not sure exactly
|
||||
1_000 iterations of PeriodCounter.inc() in 29.593ms. (when 3 event-listeners are active). I'm not sure exactly
|
||||
what a good performance would be here, but I can say the following: this means when a 1,000 events happen in a second,
|
||||
the period-counter uses up 10% of the budget. A first guess would be: this is good enough.
|
||||
the period-counter uses up 3% of the budget. A first guess would be: this is good enough.
|
||||
|
||||
@@ -106,7 +106,7 @@ def print_thoughts_about_event_evaluation():
|
||||
|
||||
1_000 iterations of PeriodCounter.inc() in {t.elapsed:.3f}ms. (when 3 event-listeners are active). I'm not sure exactly
|
||||
what a good performance would be here, but I can say the following: this means when a 1,000 events happen in a second,
|
||||
the period-counter uses up 10% of the budget. A first guess would be: this is good enough.""")
|
||||
the period-counter uses up 3% of the budget. A first guess would be: this is good enough.""")
|
||||
|
||||
|
||||
print_thoughts_about_prev_tup()
|
||||
|
||||
Reference in New Issue
Block a user