Repository navigation
Cost model for keepPolicies (CIP-0168)
#7930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>KeepPolicies - Plutus Cost Model Visualization</title> | ||
| <link rel="stylesheet" href="../shared/styles.css"> | ||
| <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script> | ||
| </head> | ||
| <body> | ||
| <nav></nav> | ||
|
|
||
| <div class="container"> | ||
| <h1>KeepPolicies Cost Model Visualization</h1> | ||
| <p> | ||
| Interactive 3D visualization of benchmark data and the fitted cost model for the | ||
| <code>KeepPolicies</code> builtin. The x-axis is the length of the policy list, the y-axis the total size | ||
| of the <code>Value</code>, and the z-axis execution time. Every benchmark point appears twice at the | ||
| same place on the floor: as a blue dot for what it measured, and as a red cross for what the model | ||
| charges for it. Where a cross sits above its dot the model overcharges that input; for a few of | ||
| the largest inputs the fitted plane sits below the measurement. The sizes are on log | ||
| axes because they are sampled log-uniformly, which leaves out the few points at p = 0 or n = 0. | ||
| </p> | ||
|
|
||
| <div class="controls" id="data-source-controls"> | ||
| <h3 id="data-source-toggle">Data Source Configuration</h3> | ||
| <div class="controls-content"> | ||
| <div class="control-group-vertical"> | ||
| <label for="branch-name">Branch name:</label> | ||
| <div class="branch-input-row"> | ||
| <input type="text" id="branch-name" placeholder="master"> | ||
| <button id="copy-link" title="Copy shareable link">Copy Link</button> | ||
| </div> | ||
| </div> | ||
| <div class="control-group-vertical"> | ||
| <label for="csv-url">CSV file URL:</label> | ||
| <input type="text" id="csv-url" style="width: 100%; font-family: monospace;"> | ||
| </div> | ||
| <div class="control-group-vertical"> | ||
| <label for="json-url">JSON file URL:</label> | ||
| <input type="text" id="json-url" style="width: 100%; font-family: monospace;"> | ||
| </div> | ||
| <div class="control-group"> | ||
| <button id="reload-data">Load Data</button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="controls" id="plot-controls"> | ||
| <h3 id="plot-controls-toggle">Plot Controls</h3> | ||
| <div class="controls-content"> | ||
| <div class="control-group"> | ||
| <input type="checkbox" id="show-model" checked> | ||
| <label for="show-model">Show model predictions</label> | ||
| </div> | ||
| <div class="control-group"> | ||
| <label for="axis-scale">Axes:</label> | ||
| <select id="axis-scale"> | ||
| <option value="log">Logarithmic</option> | ||
| <option value="linear">Linear</option> | ||
| </select> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="plot-wrapper"> | ||
| <div id="plot-container"> | ||
| <div class="loading">Loading data and generating plot...</div> | ||
| </div> | ||
|
|
||
| <div class="info-panel"> | ||
| <h3>Plot Information</h3> | ||
|
|
||
| <div class="info-section"> | ||
| <dl> | ||
| <dt>Data points:</dt> | ||
| <dd id="info-data-points">-</dd> | ||
|
|
||
| <dt>Policy list length (arg1):</dt> | ||
| <dd id="info-x-range">-</dd> | ||
|
|
||
| <dt>Value size range (arg2):</dt> | ||
| <dd id="info-y-range">-</dd> | ||
|
|
||
| <dt>Time range:</dt> | ||
| <dd id="info-time-range">-</dd> | ||
|
|
||
| <dt>Overhead:</dt> | ||
| <dd id="info-overhead">-</dd> | ||
| </dl> | ||
| </div> | ||
|
|
||
| <div class="info-section"> | ||
| <h3>The two fits</h3> | ||
| <div id="fit-comparison">-</div> | ||
| </div> | ||
|
|
||
| <div class="info-section"> | ||
| <dl> | ||
| <dt>Model type:</dt> | ||
| <dd id="info-model-type">-</dd> | ||
|
|
||
| <dt>Model formula:</dt> | ||
| <dd id="info-model-formula">-</dd> | ||
| </dl> | ||
| </div> | ||
|
|
||
| <div class="info-section"> | ||
| <dl id="info-data-sources"></dl> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <footer></footer> | ||
|
|
||
| <script src="../shared/utils.js"></script> | ||
| <script src="plot.js"></script> | ||
| </body> | ||
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| // KeepPolicies plot configuration and rendering (3D) | ||
|
|
||
| const FUNCTION_NAME = 'KeepPolicies'; // CSV uses PascalCase | ||
| const COST_MODEL_NAME = 'keepPolicies'; // JSON uses camelCase | ||
| const ARITY = 2; | ||
|
|
||
| let benchmarkData = []; | ||
| let costModel = null; | ||
| let stockModel = null; | ||
| let overhead = 0; | ||
| let showModel = true; | ||
| let axisScale = 'log'; | ||
|
|
||
| setupCostModelPage({ | ||
| slug: 'keeppolicies', | ||
| functionName: FUNCTION_NAME, | ||
| costModelName: COST_MODEL_NAME, | ||
| arity: ARITY, | ||
| render(data) { | ||
| ({ benchmarkData, costModel, overhead } = data); | ||
| // The plain `lm(t ~ x_mem + y_mem)` of models.R, refit in the browser on the same | ||
| // points the shipped fit was built from. The shipped model comes from that same fit | ||
| // (negative coefficients clamped), so the two blocks agreeing checks that the JSON | ||
| // matches the fit; the undercharge lines show where the plane sits below the data. | ||
| stockModel = fitLinearInXAndY(benchmarkData.filter(d => d.args[1] > 0), overhead); | ||
| updateInfoPanel(); | ||
| renderPlot(); | ||
| }, | ||
| setupControls | ||
| }); | ||
|
|
||
| function updateInfoPanel() { | ||
| const stats = calculateStats(benchmarkData); | ||
| document.getElementById('info-data-points').textContent = stats.dataPoints; | ||
|
|
||
| const xs = benchmarkData.map(d => d.args[0]); | ||
| const ys = benchmarkData.map(d => d.args[1]); | ||
| document.getElementById('info-x-range').textContent = | ||
| `${Math.min(...xs)} - ${Math.max(...xs)}`; | ||
| document.getElementById('info-y-range').textContent = | ||
| `${Math.min(...ys)} - ${Math.max(...ys)}`; | ||
| document.getElementById('info-time-range').textContent = stats.timeRange; | ||
| document.getElementById('info-overhead').textContent = overhead > 0 | ||
| ? `${overhead.toFixed(2)} ns (arity ${ARITY}) added to predictions` | ||
| : 'Not calculated'; | ||
|
|
||
| // Summarize both fits over the population they were built on (the y = 0 controls are | ||
| // out of the fit, so they would only distort the undercharge and overcharge lines). | ||
| const fitted = benchmarkData.filter(d => d.args[1] > 0); | ||
| document.getElementById('fit-comparison').innerHTML = | ||
| fitSummary('The shipped model (from the cost-model JSON)', | ||
| costModel, fitted, overhead, ['p', 'n']) | ||
| + fitSummary('The same least-squares fit recomputed from the CSV', | ||
| stockModel, fitted, overhead, ['p', 'n']); | ||
|
|
||
| if (costModel) { | ||
| document.getElementById('info-model-type').textContent = costModel.modelType; | ||
| document.getElementById('info-model-formula').textContent = | ||
| formatModelFormula(costModel.modelType, costModel.coefficients); | ||
| } else { | ||
| document.getElementById('info-model-type').textContent = 'Not available'; | ||
| document.getElementById('info-model-formula').textContent = 'Cost model not found'; | ||
| } | ||
| } | ||
|
|
||
| /* The two argument sizes on the floor, time up. Every benchmark point appears twice at the | ||
| same place on the floor, once as what it measured and once as what the model charges for it, | ||
| so the model is safe exactly where the red crosses sit above the blue dots. The sizes are | ||
| sampled log-uniformly, so a log floor is what spreads them out; the cost of that is the few | ||
| points at p = 0 or n = 0, which a log axis cannot place and which are out of the fit anyway. */ | ||
| function plotTraces() { | ||
| const traces = [{ | ||
| x: benchmarkData.map(d => d.args[0]), | ||
| y: benchmarkData.map(d => d.args[1]), | ||
| z: benchmarkData.map(d => d.time), | ||
| mode: 'markers', | ||
| type: 'scatter3d', | ||
| name: 'Benchmark Data', | ||
| marker: { size: 4, color: '#0033AD', opacity: 0.8 }, | ||
| hovertemplate: 'p %{x}, n %{y}<br>measured %{z:.3s} ns<extra></extra>' | ||
| }]; | ||
|
|
||
| if (showModel && costModel) { | ||
| traces.push({ | ||
| x: benchmarkData.map(d => d.args[0]), | ||
| y: benchmarkData.map(d => d.args[1]), | ||
| z: benchmarkData.map(d => modelCharge(costModel, d.args, overhead)), | ||
| mode: 'markers', | ||
| type: 'scatter3d', | ||
| name: 'Model Predictions', | ||
| marker: { size: 4, color: '#E53E3E', opacity: 0.6, symbol: 'x' }, | ||
| hovertemplate: 'p %{x}, n %{y}<br>charged %{z:.3s} ns<extra></extra>' | ||
| }); | ||
| } | ||
| return traces; | ||
| } | ||
|
|
||
| const PLOT_CONFIG = { responsive: true, displayModeBar: true, displaylogo: false }; | ||
|
|
||
| function renderPlot() { | ||
| ensurePlotPanel('plot-3d'); | ||
| const scaled = axisScale; | ||
| const suffix = axisScale === 'log' ? ', log' : ''; | ||
| Plotly.react('plot-3d', plotTraces(), { | ||
| title: { text: `${FUNCTION_NAME} - Benchmark vs Model (3D)`, font: { size: 20 } }, | ||
| scene: { | ||
| xaxis: { title: `Policy list length (p${suffix})`, gridcolor: '#E0E0E0', type: scaled }, | ||
| yaxis: { title: `Value total size (n${suffix})`, gridcolor: '#E0E0E0', type: scaled }, | ||
| zaxis: { title: `Time (ns${suffix})`, gridcolor: '#E0E0E0', type: scaled }, | ||
| camera: { eye: { x: 1.7, y: -1.7, z: 0.7 } } | ||
| }, | ||
| showlegend: true, | ||
| legend: { | ||
| x: 0.02, | ||
| y: 0.98, | ||
| bgcolor: 'rgba(255, 255, 255, 0.8)', | ||
| bordercolor: '#BDC3C7', | ||
| borderwidth: 1 | ||
| }, | ||
| margin: { t: 60, b: 10, l: 10, r: 10 }, | ||
| height: 700, | ||
| paper_bgcolor: 'rgba(0,0,0,0)' | ||
| }, PLOT_CONFIG); | ||
| } | ||
|
|
||
| function setupControls() { | ||
| document.getElementById('show-model').addEventListener('change', e => { | ||
| showModel = e.target.checked; | ||
| renderPlot(); | ||
| }); | ||
| document.getElementById('axis-scale').addEventListener('change', e => { | ||
| axisScale = e.target.value; | ||
| renderPlot(); | ||
| }); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
.../changelog.d/20260828_144508_yuriy.lazaryev_issue_2310_keep_policies_costing.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ### Added | ||
|
|
||
| - A cost model for the `keepPolicies` builtin ([CIP-0168](https://cips.cardano.org/cip/CIP-0168)), with five new cost model parameters. Linear in the length of the policy list and in the total size of the `Value`. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.