Repository navigation
Conversation
b7c3892 to
ae4a298
Compare
|
ae4a298 to
e491998
Compare
e491998 to
3dc9dba
Compare
3dc9dba to
b181995
Compare
b181995 to
ec470fc
Compare
Every point of the main sweep is the costliest input for its pair of sizes: the builtin skips the part of the outer map the list cannot reach, so the pairs that cost anything are the pairs the list names, and packing them into as few policies as the list covers puts all of them in reach. The list length and the total size are sampled log-uniformly and independently, and two sweeps hold both fixed while varying what a cost model cannot see: the policy count and how much of the list hits. Both move the cost, so no model of the two sizes is right everywhere, and these sweeps are what measures how far wrong it gets. The shape sweep runs against a short list as well as a long one, because the list term swamps the `Value` term once the list is long. The points with an empty `Value` double as a control on the rig: the builtin returns before it looks at the list there, so the same list costs about a hundredth of what it costs against a `Value` that is not empty. A benchmark that floated the list work out of the measured loop would show no such gap. Shares the log-uniform sampler and the `Value` generator with the `policies` benchmark, which had its own copies of both.
Linear in the length of the policy list and in `Value.totalSize`, which is what `instance ExMemoryUsage Value` already reports, so the denotation needs no size wrapper. Five parameters, fitted against semantics variant E, which is what PlutusV3 and PlutusV4 map to. The fit undercharges 8 of its 126 points, at worst by 1.28x and in seven of the eight by between 1.01x and 1.18x. That is the file's normal condition: scored the same way against their own rows, the shipped `addInteger` undercharges 48% of its 225 rows, `appendByteString` 59% of 441 and `valueContains` 49% of 1158, none by more than 1.2x. Points with an empty outer map are left out of the fit, because the builtin returns before it looks at the list and an element costs single-digit nanoseconds there against the hundreds the real work costs. Scoring on only the 120 rows the fit sees gives 7 undercharges rather than 8. Memory is six words per new outer-map node, at most one per pair in the result: the result shares the inner maps wholesale and the policy ids by pointer, so the outer spine is the only new structure. Implements IntersectMBO/plutus-private#2310.
One 3D plot: list length against `Value.totalSize` against time, with the measured points in blue and the model's charge for the same inputs in red. The spread at a fixed pair of sizes is what no model of those two numbers can capture, and in three dimensions it reads straight off the plot. The page computes a least-squares fit from the CSV it has already loaded and reports it beside the shipped coefficients, so the figures follow whatever data the page is pointed at rather than the JSON alone. `fitLinearInXAndY` in the shared code does that and reproduces what `models.R` gets from the same rows. The plot panel is created in `renderPlots` rather than declared in the page, because the shared loader replaces the contents of `#plot-container` with its own status message before it calls the page's render. Every other page targets that container itself, so nothing there noticed.
ec470fc to
c83cd63
Compare
kwxm
left a comment
There was a problem hiding this comment.
I started reviewing this a few days ago, but I think it's going to change so I'll come back and review it properly later. In the meantime, here's a couple of (not very important) comments from my partial review.
| ## policy-filter builtins, which return before they look at the list when the `Value` is | ||
| ## empty, so the y = 0 points cost the machine sizing the list rather than the builtin | ||
| ## doing its work, and one slope cannot price both. | ||
| linearInXAndYNonzeroY <- function (fname) { |
There was a problem hiding this comment.
Maybe this doesn't need to be in a function of its own: the cost model for keepPolicies could just include this verbatim since it's the only buitlin that uses it. Currently there are generic functions for fitting models which are constant or linear in one variable, but I think that for two-variable models each builtin has its own function that does all of the work and we haven't tried to factor out any commonalities. That's possibly bad practice, but I think it's happened because we've added the fitting functions as we've added new builtins and the models have all tended to be slightly different, so there wasn't much to be gained by abstraction. Maybe we should look again and see if there's any code that could be shared. Looking at the code for fitting the multi-variable models, many of them filter out things of size 0; maybe we should just do this for everything because the zero-sized inputs tend to correspond to special cases that may be faster than the typical case (like appending an empty bytestring, where no work is required), so they may drag the model for typical inputs down a bit.
| } | ||
| } | ||
|
|
||
| /* The two argument sizes on the floor, time up. Every benchmark point appears twice at the |
There was a problem hiding this comment.
What's the floor? The x-y plane at the bottom? "Every benchmark point appears twice at the same place on the floor" makes it sound as if those points are actually on the plane, not above it.
|
Superseded by #7954, which collapses this stack into a single PR with a rebuilt history: the cost model, the benchmark generator and the Notes introduced here were all replaced further up the stack. Kenneth's two comments are addressed there — the zero-sized rows are dropped from the fit in the shared |
Adds the cost model for
keepPolicies, five parameters: CPU linear in the length of the policy list and inValue.totalSize, memory linear inValue.totalSize. The denotation takes the plainValue, becauseinstance ExMemoryUsage Valuealready reportstotalSize.ValueOuterSizeis not the measure here even thoughkeepPolicieslooks likepoliciesfrom the outside: it reports the policy count, so aValueof one policy holding 10000 tokens would be measured as size 1 whilepack'folds all 10000 pairs.Memory is six words per new outer-map node, at most one per pair in the result. The result shares the inner maps wholesale and the policy ids by pointer, so the outer spine is the only new structure.
Implements IntersectMBO/plutus-private#2310.