scripts: csv.py: Added delta expr

This is the inverse of accumulate, returning the difference between
subsequent results. In theory accumulate(delta(x)) and
delta(accumulate(x)) are noops.

This is particularly useful for normalizing our bench n value in
scripts. It's the only value still returned as a cumulative measurement,
which is a bit inconsistent, but necessary for uniquely identifying
probe steps.
This commit is contained in:
Christopher Haster
2026-02-02 12:19:13 -06:00
parent 484b7dd1e8
commit 5dc88e3e00

View File

@ -816,12 +816,12 @@ class CsvExpr:
fields.get(v.a) for v in self)
x = state.get(k)
if x is None:
x = 0
z = 0
else:
x += 1
z = x + 1
# keep track of unique enumerate state
state[k] = x
return CsvInt(x)
state[k] = z
return CsvInt(z)
@func('accumulate', 'a[, *by]')
class Accumulate(Expr):
@ -852,12 +852,48 @@ class CsvExpr:
fields.get(v.a) for v in it.islice(self, 1, None))
x = state.get(k)
if x is None:
x = y
z = y
else:
x += y
z = x + y
# keep track of unique accumulate state
state[k] = x
return x
state[k] = z
return z
@func('delta', 'a[, *by]')
class Delta(Expr):
"""A [per by] difference between subsequent results"""
def fields(self):
# don't typecheck by fields
return self.a.fields()
def type(self, types={}):
# don't typecheck, but make sure we can read by fields
t = self.a.type(types)
for v in it.islice(self, 1, None):
if not isinstance(v, CsvExpr.Field):
raise CsvExpr.Error("complicated by field? %s" % v)
return t
def fold(self, types={}):
# don't typecheck by fields
return self.a.fold(types)
def eval(self, fields={}, state=None):
y = self.a.eval(fields, state)
if state is None:
return y
# compute delta
k = ('delta', id(self)) + tuple(
fields.get(v.a) for v in it.islice(self, 1, None))
x = state.get(k)
if x is None:
z = y
else:
z = y - x
# keep track of unique delta state
state[k] = y
return z
# functions
@func('ratio', 'a')